Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

i was trying to write a full detailed answer to the following question: Why does "Dispose" work, and not "using(var db = new DataContext())"?

so I set up my project that include:

Departments and employees using entity framework

so my action method was this:

public ActionResult Index()
    {

        IEnumerable<department> d;
        using (var ctx = new ApplicationDbContext())
        {

            d = ctx.departments;

        }


        return View(d);
}

it is natural to expect that this will result in the common error :

The operation cannot be completed because the DbContext has been disposed

and when i wanted to resolve it I did the following [to force eager loading rather than easy loading] :

 public ActionResult Index()
    {

        IEnumerable<department> d;
        using (var ctx = new ApplicationDbContext())
        {

            d = ctx.departments.toList();

        }


        return View(d);
}

so I was trying to understand things under the hood and looked at the return type of the View() method. and I reached to the following 'Correct' assumptions:

1- the model [d] is called in lazy loading fashion inside the using statement.

2- so when the model [d] is sent to the view for generating the page the DbContext is already disposed by the last curly bracket of the using statement.

3- we solve this situation by sending the model [d] to the view with eager loading fashion.

then I continued my assumptions that proved to be 'wrong' as following:

4- since the View() method is returning ViewResult object which is an ActionResult also..then I can generate this object inside the using statement and then return it to the user.

so I did the following:

public ActionResult Index()
    {

        ActionResult myView;

        using (var ctx = new ApplicationDbContext())
        {

            IEnumerable<department> d = ctx.departments;

            myView =  View(d);

        }


        return myView;
    }

so I told myself now when I run it , the ViewResult object [ myView] will be already created and will be returned to the user and No Error will be encountered.

However I was surprised that the same error occured :

The operation cannot be completed because the DbContext has been disposed

I was astonished how this lazy loading is really lazy and load only at the last moment.

So I continued my 'wrong' assumptions as follow:

5- may be I need to force the View() method to excute the result inside the using statement. so I used the method ExecuteResult(ControllerContext).

and now I thought I can run the action method without any error but again the same error occured:

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

So my question now is:

where in the MVC framework does the excution of lazy loading query occur !!

or let me rephrase my question as follow:

Why did View(d) method iterate over the [d] object when it is out of the using statment, and not when the view(d) method is inside the using Statement.

I just need to understand why my assumptions were wrong .. Thanx in advanced

like image 882
stackunderflow Avatar asked Oct 01 '22 22:10

stackunderflow


1 Answers

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]

enter image description here

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:

enter image description here


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 :)

like image 163
stackunderflow Avatar answered Oct 06 '22 01:10

stackunderflow