Deep understanding of lazy loading and disposing error in MVC .net

梦想与她 提交于 2019-12-04 08:36:55

Ok. I found a very convincing answer as follow:

I started to read about the MVC5 life-cycle and found many articles on the net. one of them is the following link: http://www.dotnet-tricks.com/Tutorial/mvc/TbR0041112-Asp.net-MVC-Request-Life-Cycle.html so I copied the picture and added my comment on it as the following [courtesy: www.dotnet-tricks.com]

then I read in another article [ here: http://www.codemag.com/Article/1312081] , how to render the view to string and returned that as the return type of the action method. so that I may be able to use the lazy loading and render the view while still inside the using statement.

so all I did was the following change to my action method [ explanation is included as comments]

 // GET: /dept/
    public string  Index()
    {

        IView myView;
        string result;
        using (var ctx = new ApplicationDbContext())
        {
            //my model brought using the dbContext
            IEnumerable<department> 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<department> object
            myView.Render(vc, tw);

            result = tw.ToString();// the rendered View is now written as string to the result
            tw.Dispose();
        }

        return result;
    }
}

and I was happy to see that my page rendered successfully without that famous disposing error; see the result:


So to sum it up:

the answer to my question is :

when you return a ViewResult or ActionResult from your action method; the view is still not rendered. and once it reach in the pipeline to the ViewEngine and the ViewEngine trigger the method .Render(), it is at that time the lazy loading object will need the dbContext and will result in the famous Disposing error of the dbContext. I also showed how can you render the View inside the action method itself. and even inside the using statement of the dbContext; and I could escape that disposing error.

thank you for everyone :)

Many of the LINQ methods will stream the sequence, some require the whole sequence to be buffered and operated upon, but if you do nothing to the sequence, it will not exist in memory.

IEnumerable<T> is an interface that allows for deferred execution. In fact, deferred execution is what makes LINQ efficient at all. Now, your assumptions were all correct, but you're missing one key concept: lazy loading implementation in the form of deferred execution in .NET.

Read my blog post on LINQ and deferred execution to learn about how forcing an enumeration evaluates the deferred code immediately. The reason your first sample worked was because you called ToList() which forces the enumeration and executes the code. Assignment in the second case to the view object remained deferred as it was not enumerated by assigning the result to View<IEnumerable<department>>

You could however do this inside of the using block to force the code to execute before the dispose is done:

IEnumerable<department> d = ctx.departments;

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

To answer your question about the MVC pipeline: the MVC pipeline does not evaluate the object which you place in the ViewResult, except to cast it appropriately for your View itself to use it. It therefore never performs the enumeration and forces the execution of the code. What does this is your calls within your View... Things like foreaching your Model in this case. So, by the time your view is executing, your using statement has long been disposed, so the deferred call fails.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!