关于实体框架上下文生命周期的问题

Jon*_*ood 22 c# asp.net-mvc entity-framework idisposable using

我对ASP.NET MVC应用程序中实体框架上下文的期望生命周期有一些疑问.是不是最好尽可能在最短的时间内保持上下文?

请考虑以下控制器操作:

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = context.MyTable;
    }

    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将不起作用,因为在视图呈现页面时,实体框架上下文已超出范围.其他人如何构建上面的代码?

Bri*_*per 51

让我们争议吧!

我不同意一般MVC + EF的共识,即在整个请求中保持上下文活动是一件好事,原因有很多:

性能低下 您知道创建新数据库上下文的成本是多少吗?好吧......" DataContext很轻,创建起来并不昂贵 "来自MSDN

让IoC出错并且它看起来很好......直到你上线 如果你设置了你的IoC容器来为你处理你的上下文并且你弄错了,你真的错了.我已经两次看到从IoC容器创建的大量内存泄漏并不总是正确地处理上下文.在正常的并发用户级别中,服务器开始崩溃之前,您不会意识到自己设置错误了.它不会在开发中发生,所以做一些负载测试!

意外延迟加载 您可以返回最新文章的IQueryable,以便您可以在主页上列出它们.有一天,要求其他人显示相应文章旁边的评论数量.所以他们在View中添加了一些简单的代码来显示注释计数......

@foreach(var article in Model.Articles) {
    <div>
        <b>@article.Title</b> <span>@article.Comments.Count() comments</span>
    </div>
}
Run Code Online (Sandbox Code Playgroud)

看起来很好,工作正常.但实际上您没有在返回的数据中包含注释,因此现在这将为循环中的每篇文章进行新的数据库调用.选择N + 1问题.10篇文章= 11个数据库调用.好吧,所以代码是错误的,但这是一个容易犯的错误,所以它会发生.

您可以通过在数据层中关闭上下文来防止这种情况发生.但是,在article.Comments.Count()上,代码是否会因NullReferenceException而中断?是的,它会强制您编辑数据层以获取View层所需的数据.这应该是怎样的.

代码味道 从View中访问数据库有一些问题.你知道IQueryable实际上并没有真正命中数据库,所以忘了那个对象.确保数据库在离开数据层之前已被命中.

所以答案

你的代码应该(在我看来)像这样

数据层:

public List<Article> GetArticles()
{
    List<Article> model;

    using (var context = new MyEntities())
    {
        //for an example I've assumed your "MyTable" is a table of news articles
        model = (from mt in context.Articles
                select mt).ToList();
        //data in a List<T> so the database has been hit now and data is final
    }

    return model;
}
Run Code Online (Sandbox Code Playgroud)

控制器:

public ActionResult Index()
{
    var model = new HomeViewModel(); //class with the bits needed for you view
    model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

一旦你完成了这个并理解了这一点,那么也许你可以开始尝试拥有一个IoC容器句柄上下文,但绝对不是之前.警告我 - 我看过两次大规模的失败:)

但老实说,做你喜欢的,编程很有趣,应该是一个偏好的问题.我只是告诉你我的.但无论你做什么,都不要开始在每个控制器或每个请求中使用IoC上下文,因为"所有很酷的孩子都在这样做".这样做是因为你真正关心它的好处并理解它是如何正确完成的.

  • 我认为值得引用MSDN的整个段落:"一般来说,DataContext实例设计为持续一个'工作单元',但是你的应用程序定义了这个术语.DataContext是轻量级的,创建起来并不昂贵.典型的LINQ SQL应用程序在方法范围内创建DataContext实例,或者作为表示相关数据库操作的逻辑集的短期类的成员." (3认同)

Not*_*ple 6

我同意每个请求一个上下文,我们通常通过绑定上下文来实现这一点.使用Ninject的InRequestScope,它运行得很好,是:

Bind<MyContext>().ToSelf().InRequestScope();
Run Code Online (Sandbox Code Playgroud)

最好的做法是将集合枚举为尽可能接近查询,即:

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = (from mt in context.MyTable
                select mt).ToArray();
    }
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

这有助于您避免在视图中无意中增加查询.