本文介绍了关于 Entity Framework Context Lifetime 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

考虑以下控制器操作:

public ActionResult Index(){IEnumerable模型;使用 (var context = new MyEntities()){模型 = context.MyTable;}返回视图(模型);}

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

解决方案

让我们争论起来!

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

低性能提升你知道创建一个新的数据库上下文有多昂贵吗?嗯……DataContext 是轻量级的,创建起来并不昂贵",来自 MSDN

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

意外延迟加载您返回最近文章的 IQueryable,以便您可以在主页上列出它们.有一天,有人会被要求在相应文章旁边显示评论数量.所以他们添加了一些简单的代码到视图来显示评论数,就像这样......

@foreach(Model.Articles 中的 var 文章) {<div><b>@article.Title</b><span>@article.Comments.Count() 评论</span>

}

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

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

代码异味从您的视图中访问数据库是有问题的.你知道一个 IQueryable 还没有真正命中数据库,所以忘记那个对象.确保您的数据库在离开数据层之前被命中.

答案是

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

数据层:

公共列表获取文章(){列表模型;使用 (var context = new MyEntities()){//举个例子,我假设你的MyTable"是一个新闻文章表模型 =(来自上下文中的 mt.Articles选择 mt).ToList();//列表中的数据所以现在数据库已经被命中,数据是最终的}退货模式;}

控制器:

public ActionResult Index(){var model = new HomeViewModel();//使用您查看所需的位进行类模型.文章 = _dataservice.GetArticles();//无关_dataService是如何初始化的返回视图(模型);}

一旦你做到了这一点并理解了这一点,那么也许你可以开始尝试让 IoC 容器处理上下文,但绝对不是之前.警告我 - 我见过两次大规模失败:)

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

I have some questions about the desired lifetime of an Entity Framework context in an ASP.NET MVC application. Isn't it best to keep the context alive for the shortest time possible?

Consider the following controller action:

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

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

    return View(model);
}

The code above won't work because the Entity Framework context has gone out of scope while the view renders the page. How would others structure the code above?

解决方案

Let's get controversial!

I disagree with the general MVC + EF consensus that keeping a context alive throughout the entire request is a good thing for a number of reasons:

Low performance increaseDo you know how expensive creating a new database context is? Well... "A DataContext is lightweight and is not expensive to create" that's from MSDN

Get the IoC wrong and it'll seem fine.. until you go liveIf you set up your IoC container to dispose of your context for you and you get it wrong, you really really get it wrong. I've twice now seen massive memory leaks created from an IoC container not always disposing of a context correctly. You won't realise you've set it up wrong until your servers start crumbling during normal levels of concurrent users. It won't happen in development so do some load tests!

Accidental lazy loadingYou return an IQueryable of your most recent articles so that you can list them on your homepage. One day someone else is asked to show the number of comments next to the respective article. So they add a simple bit of code to the View to show the comment count like so...

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

Looks fine, works fine. But actually you didn't include the comments in your returned data so now this will make a new database call for each article in the loop. SELECT N+1 issue. 10 article = 11 database calls. Okay so the code is wrong but it is an easy mistake to make so it will happen.

You can prevent this by shutting your context down in you data layer. But won't the code break with a NullReferenceException on the article.Comments.Count() ? Yes it will so it will force you to edit the Data layer to get the data needed for the View layer. This is how is should be.

Code smellThere is just something wrong about hitting the database from your View. You know that an IQueryable hasn't actually hit the database yet right so forget that object. Make sure your database is hit before it leaves your data layer.

So the answer

Your code should be (in my opinion) like this

DataLayer:

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;
}

Controller:

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);
}

Once you have done this and understand this then perhaps you can begin experimenting with having an IoC container handle context but definitely not before. Head my warning - I've seen two large scale failures :)

But honestly do what you like, programming is fun and should be a matter of preference. I'm just telling you mine. But whatever you do, don't start using IoC context per controller or per request just because "all the cool kids are doing it." Do it because you really truly care about it's benefits and understand how it's done correctly.

这篇关于关于 Entity Framework Context Lifetime 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 22:33