关于entity framework上下文生命周期的问题

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

请考虑以下控制器操作:

public ActionResult Index() { IEnumerable model; using (var context = new MyEntities()) { model = context.MyTable; } return View(model); } 

上面的代码不起作用,因为在视图呈现页面时,entity framework上下文已超出范围。 其他人如何构建上面的代码?

让我们争议吧!

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

低性能增加您知道创建新数据库上下文的成本是多少吗? 嗯……“ DataContext很轻,创建起来并不昂贵 ”来自MSDN

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

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

 @foreach(var article in Model.Articles) { 
@article.Title @article.Comments.Count() comments
}

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

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

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

所以答案

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

数据层:

 public List
GetArticles() { List
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 so the database has been hit now and data is final } return model; }

控制器:

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

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

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

我同意每个请求一个上下文,我们通常通过使用Ninject绑定上下文.InRequestScope,这非常有效,是:

 Bind().ToSelf().InRequestScope(); 

也是一个很好的做法,可以将集合枚举为尽可能接近查询,即:

 public ActionResult Index() { IEnumerable model; using (var context = new MyEntities()) { model = (from mt in context.MyTable select mt).ToArray(); } return View(model); } 

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

首先,您应该考虑将数据库访问权限分配给不同的类。

其次,我最喜欢的解决方案是使用“每个请求一个上下文”(如果你使用的是MVC,我相信它是每个控制器的一个上下文)。

请求编辑:

看看这个答案,也许它对你也有帮助。 请注意我正在使用webforms,因此目前无法在MVC中validation它,但它可能对您有所帮助或至少给您一些指示。 https://stackoverflow.com/a/10153406/1289283

此dbcontext的一些示例用法:

 public class SomeDataAccessClass { public static IQueryable GetAllProducts() { var products = from o in ContextPerRequest.Current.Products select o; return products; } } 

然后你可以做这样的事情:

 public ActionResult Index() { var products = SomeDataAccessClass.GetProducts(); return View(products); } 

简单吧? 您不必再担心处理上下文,只编写您真正需要的代码。

有些人喜欢通过添加UnitOfWork模式或者IoC容器来进一步增加一些东西…但我更喜欢这种方法,因为它的简单性。

你可以使用LINQ的.ToList()扩展方法:

 public ActionResult Index() { IEnumerable model; using (var context = new MyEntities()) { model = (from mt in context.MyTable select mt).ToList(); } return View(model); }