惰性和急切加载 MVC 5
Lazy & eager Loading MVC 5
我有以下代码,我尝试让所有学生(UserTypeID =2)
using SchoolData;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace SchoolAPI.Controllers
{
public class StudentsController : ApiController
{
public List<User> Get()
{
using(SchoolEntities DB = new SchoolEntities())
{
var L = DB.Users.Where(u => u.UserTypeID == 2).ToList();
return L;
}
}
public User Get(int id)
{
using(SchoolEntities DB = new SchoolEntities())
{
return DB.Users.Where(u => u.UserTypeID == 2).FirstOrDefault(u => u.UID == id);
}
}
}
}
当我 运行 启用延迟加载的代码时,我尝试通过函数 Get() 获取所有学生,它抛出错误:
"Message": "An error has occurred.",
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null,
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Error getting value from 'City' on 'System.Data.Entity.DynamicProxies.User_43D4A249734A75DBA5AC314F4FE462E834BDC252CC9384BF940FE65C74CE3D08'
和
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
当我尝试禁用懒惰模式并重新运行时,它正确地获取了对象,但有一些额外的不需要的参数如下:
{
"City": null,
"CourseDetails": [],
"StudentsCourses": [],
"UserType": null,
"UID": 2,
"FName": "Ahmed",
"LName": "Mano",
"Birthdate": "1995-05-27T00:00:00",
"CityID": 1,
"UserTypeID": 2
}
而且我既不需要 CourseDetails、StudentsCourses、City 也不需要 UserType
当对象序列化为 json 时,这些属性是序列化的,无论您是否在客户端使用它们。由于序列化发生在退出 using 块之后,因此在释放上下文后访问属性。您可以执行以下三项操作之一:
用[JsonIgnore]
属性标记未使用的属性。详情见here
创建第二个 class,它只包含您在客户端中需要的属性,并使用 Select
将 User
投影到这个新的更具体的对象。或者使用 auromapper 自动进行映射。关于 auromapper this 可能是一篇帮助您入门的好文章
为每个控制器创建一个上下文,在承包商中对其进行初始化,并且仅将其置于对控制器处置方法的重写中。这将确保上下文在序列化期间可用。
从性能的角度来看,2 是最好的,因为您只发送您需要的内容,并且您可以更灵活地为每个操作定制输出(也许其他操作需要这些字段)。 3 是最简单的实现方式,但是您发送了很多不需要的数据,并且不必要地访问了数据库。 1 报价和简单的修复,直到其他人向模型添加新的 属性,此外,您最终可能会在客户端中获得不仅您不需要,而且不应该到达客户端的数据(密码,其他可能添加到模型中的敏感信息)
Titian Cernicova 完全正确,我只想补充一件事:如果你 return 来自 WebAPI 控制器的实体对象,你可能有内存问题并且可能是服务器上的内存泄漏,永远不要那样做
因此,一个好的做法是创建一个具有您需要的属性的数据传输对象,例如:
public class DtoUser
{
public int UID { get; set; }
public string FName { get; set; }
public DateTime Birthdate { get; set; }
public int CityID { get; set; }
public int UserTypeID { get; set; }
}
将您的实体对象投射到此和 return。
我有以下代码,我尝试让所有学生(UserTypeID =2)
using SchoolData;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace SchoolAPI.Controllers
{
public class StudentsController : ApiController
{
public List<User> Get()
{
using(SchoolEntities DB = new SchoolEntities())
{
var L = DB.Users.Where(u => u.UserTypeID == 2).ToList();
return L;
}
}
public User Get(int id)
{
using(SchoolEntities DB = new SchoolEntities())
{
return DB.Users.Where(u => u.UserTypeID == 2).FirstOrDefault(u => u.UID == id);
}
}
}
}
当我 运行 启用延迟加载的代码时,我尝试通过函数 Get() 获取所有学生,它抛出错误:
"Message": "An error has occurred.",
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null,
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Error getting value from 'City' on 'System.Data.Entity.DynamicProxies.User_43D4A249734A75DBA5AC314F4FE462E834BDC252CC9384BF940FE65C74CE3D08'
和
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
当我尝试禁用懒惰模式并重新运行时,它正确地获取了对象,但有一些额外的不需要的参数如下:
{
"City": null,
"CourseDetails": [],
"StudentsCourses": [],
"UserType": null,
"UID": 2,
"FName": "Ahmed",
"LName": "Mano",
"Birthdate": "1995-05-27T00:00:00",
"CityID": 1,
"UserTypeID": 2
}
而且我既不需要 CourseDetails、StudentsCourses、City 也不需要 UserType
当对象序列化为 json 时,这些属性是序列化的,无论您是否在客户端使用它们。由于序列化发生在退出 using 块之后,因此在释放上下文后访问属性。您可以执行以下三项操作之一:
用
[JsonIgnore]
属性标记未使用的属性。详情见here创建第二个 class,它只包含您在客户端中需要的属性,并使用
Select
将User
投影到这个新的更具体的对象。或者使用 auromapper 自动进行映射。关于 auromapper this 可能是一篇帮助您入门的好文章为每个控制器创建一个上下文,在承包商中对其进行初始化,并且仅将其置于对控制器处置方法的重写中。这将确保上下文在序列化期间可用。
从性能的角度来看,2 是最好的,因为您只发送您需要的内容,并且您可以更灵活地为每个操作定制输出(也许其他操作需要这些字段)。 3 是最简单的实现方式,但是您发送了很多不需要的数据,并且不必要地访问了数据库。 1 报价和简单的修复,直到其他人向模型添加新的 属性,此外,您最终可能会在客户端中获得不仅您不需要,而且不应该到达客户端的数据(密码,其他可能添加到模型中的敏感信息)
Titian Cernicova 完全正确,我只想补充一件事:如果你 return 来自 WebAPI 控制器的实体对象,你可能有内存问题并且可能是服务器上的内存泄漏,永远不要那样做
因此,一个好的做法是创建一个具有您需要的属性的数据传输对象,例如:
public class DtoUser
{
public int UID { get; set; }
public string FName { get; set; }
public DateTime Birthdate { get; set; }
public int CityID { get; set; }
public int UserTypeID { get; set; }
}
将您的实体对象投射到此和 return。