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

有帮助吗?

解决方案

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

其他提示

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top