【问题标题】:Lazy & eager Loading MVC 5懒惰和急切的加载 MVC 5
【发布时间】:2018-01-22 13:28:53
【问题描述】:

我有以下代码,我尝试获取所有学生(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

【问题讨论】:

  • 因为序列化器在using语句引入的作用域结束数据库交互之后任意遍历对象。它与您的查询没有直接关系,因为它们序列化程序无论如何都必须具体化整个图。通常使用专用的响应类型来处理这种情况。一个更广泛的看待这个问题的方法是思考在延迟加载下访问聚合属性的开放连接的要求在类型系统中是无法表达的,至少这是看待它的一种方式。

标签: c# entity-framework asp.net-mvc-5 lazy-loading eager-loading


【解决方案1】:

当对象被序列化为 json 时,这些属性是序列化的天气,您是否在客户端使用它们。由于在退出 using 块后发生序列化,因此在释放上下文后访问属性。您可以做以下三件事之一:

  1. [JsonIgnore] 属性标记未使用的属性。详情见here

  2. 创建第二个类,该类仅包含您在客户端中需要的属性,并使用SelectUser 投影到这个新的更具体的对象。或者使用 auromapper 自动进行映射。关于 auromapper this 可能是一篇让您入门的好文章

  3. 为每个控制器创建一个上下文,在承包商中对其进行初始化,然后仅在对控制器 dispose 方法的覆盖中将其处置。这将确保在序列化期间上下文可用。

从性能的角度来看,2 是最好的,因为您只发送您需要的内容,并且您可以更灵活地为每个操作定制输出(也许其他操作需要这些字段)。 3 是最简单的实现,但是您正在发送大量不需要的数据,并且不必要地访问数据库。 1 提供并轻松修复,直到其他人向模型添加新属性,此外,您最终可能会在客户端中获得不仅您不需要而且不应到达客户端的数据(密码、其他敏感信息可能会添加到模型中)

【讨论】:

    【解决方案2】:

    Titian Cernicova 是完全正确的,我只想补充一件事:如果你从 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; }
    }
    

    将您的实体对象投射到此并返回。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-14
      • 1970-01-01
      • 2012-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多