深入理解延迟加载和处理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
语句一直处理掉,因此延迟调用失败。