深入理解延迟加载和处理MVC .net中的错误

我试图写一个完整详细的答案来解决以下问题: 为什么“Dispose”工作,而不是“使用(var db = new DataContext())”?

所以我设置了我的项目,包括:

部门和员工使用entity framework

所以我的行动方法是这样的:

public ActionResult Index() { IEnumerable d; using (var ctx = new ApplicationDbContext()) { d = ctx.departments; } return View(d); } 

很自然地会发现这会导致常见错误:

 The operation cannot be completed because the DbContext has been disposed 

当我想解决它时,我做了以下[强制加载而不是轻松加载]:

  public ActionResult Index() { IEnumerable d; using (var ctx = new ApplicationDbContext()) { d = ctx.departments.toList(); } return View(d); } 

所以我试图了解引擎盖下的内容并查看View()方法的返回类型。 我达到了以下’ 正确 ‘的假设:

1-在using语句中以延迟加载方式调用模型[d]。

2-因此,当模型[d]被发送到视图以生成页面时,DbContext已经被using语句的最后一个大括号括起来了。

3-我们通过以热切的加载方式将模型[d]发送到视图来解决这种情况。

然后我继续我的假设被certificate是“ 错误的 ”如下:

4-因为View()方法返回的是ViewResult对象,它也是一个ActionResult ..然后我可以在using语句中生成这个对象,然后将它返回给用户。

所以我做了以下事情:

 public ActionResult Index() { ActionResult myView; using (var ctx = new ApplicationDbContext()) { IEnumerable d = ctx.departments; myView = View(d); } return myView; } 

所以我现在告诉自己,当我运行它时,ViewResult对象[myView]将被创建并将返回给用户并且将遇到No Error。

但是我很惊讶发生了同样的错误:

 The operation cannot be completed because the DbContext has been disposed 

我很惊讶这个懒惰的加载是如何真正的懒惰,只在最后一刻加载。

所以我继续我的’ 错误 ‘假设如下:

5-可能是我需要强制View()方法在using语句中执行结果。 所以我使用了方法ExecuteResult(ControllerContext)

现在我以为我可以运行动作方法没有任何错误,但同样的错误发生了:

 The operation cannot be completed because the DbContext has been disposed. 

所以现在我的问题是:

在MVC框架中执行延迟加载查询的情况!

或者让我重新解释我的问题如下:

为什么View(d)方法在[d]对象超出using状态时迭代,而不是在view(d)方法在using语句内时。

我只需要理解为什么我的假设是错误的.. Thanx在先进

好。 我找到了一个非常有说服力的答案如下:

我开始阅读有关MVC5生命周期的内容,并在网上发现了很多文章。 其中一个是以下链接: http : //www.dotnet-tricks.com/Tutorial/mvc/TbR0041112-Asp.net-MVC-Request-Life-Cycle.html所以我复制了图片并添加了我对它的评论如下[礼貌:www.dotnet-tricks.com]

在此处输入图像描述

然后我读了另一篇文章[这里: http : //www.codemag.com/Article/1312081] ,如何将视图呈现为字符串并将其作为action方法的返回类型返回。 这样我就可以使用延迟加载并在仍然在using语句中时呈现视图。

所以我所做的就是对我的行动方法进行以下更改[解释作为评论包含]

  // GET: /dept/ public string Index() { IView myView; string result; using (var ctx = new ApplicationDbContext()) { //my model brought using the dbContext IEnumerable d = ctx.departments; // now because I want to render the View here [forcibly] and not waiting //for the normal MVC pipeline to render my View I had to jump to the ViewEngine //and ask it to render my View [while i am still inside this using statement] // so referring to the excellent article on :http://www.codemag.com/Article/1312081 //I did the following: ControllerContext.Controller.ViewData.Model = d; ViewEngineResult viewEngResult = ViewEngines.Engines.FindView(ControllerContext, "~/Views/dept/Index.cshtml", null); myView = viewEngResult.View; //till this point the View is not rendered yet StringWriter tw = new StringWriter();// used to render the View into string ViewContext vc = new ViewContext(ControllerContext, myView, ControllerContext.Controller.ViewData, ControllerContext.Controller.TempData, tw); //it is the following method .Render(viewContext, textWriter) that will start iterating on the IEnumerable object myView.Render(vc, tw); result = tw.ToString();// the rendered View is now written as string to the result tw.Dispose(); } return result; } } 

我很高兴看到我的页面成功呈现没有那个着名的处理错误; 看到结果:

在此处输入图像描述


总结一下:

我的问题的答案是:

从action方法返回ViewResult或ActionResult时; 视图仍未呈现。 一旦它到达ViewEngine的管道并且ViewEngine触发方法.Render(),那么延迟加载对象将需要dbContext并将导致dbContext的着名的Disposing错误。 我还展示了如何在Action方法本身内呈现View。 甚至在dbContext的using语句中; 我可以逃避处理错误。

谢谢大家:)

许多LINQ方法将流式传输序列,有些需要对整个序列进行缓冲和操作,但如果您对序列不执行任何操作,则它将不会存在于内存中。

IEnumerable是一个允许延迟执行的接口。 实际上,延迟执行是使LINQ高效的原因。 现在,您的假设都是正确的,但您缺少一个关键概念:在.NET中延迟执行的延迟加载实现。

阅读我关于LINQ和延期执行的博客文章 ,了解如何强制枚举立即评估延迟代码。 你的第一个样本工作的原因是因为你调用了ToList()来强制枚举并执行代码。 第二种情况下对视图对象的赋值仍然是延迟的,因为它没有通过将结果赋值给View>来枚举

但是,您可以在using块内部执行此操作以强制代码在dispose完成之前执行:

 IEnumerable d = ctx.departments; // ToList() here forces the evaluation of the deferred code myView = View(d.ToList()); 

要回答有关MVC管道的问题:MVC管道不会评估您放置在ViewResult的对象,除非为View自身适当地投射它以使用它。 因此,它永远不会执行枚举并强制执行代码。 这是您在视图中的调用…在这种情况下,例如foreach Model 。 因此,在您的视图执行时,您的using语句一直处理掉,因此延迟调用失败。