9

I'm creating a forum which is made up of topics, which are made up of messages.

When I try to implement the topic View in my Controller with:

public ActionResult Topic(int id) //Topic Id
{
    using (var db = new DataContext())
    {
        var topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").FirstOrDefault(x => x.Id == id);

        //include the messages for each topic, and when they were created so that the last message can be displayed on the topic page
        return topic != null ? View(topic) : View();
    }
}

I get this error when I try to view the topic page:

ObjectDisposedException was unhandled by user code

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

The error doesn't seem specific to a certain line, as when I remove the offending line, the same error apperars earlier on.

I have solved this by using:

DataContext db = new DataContext();

at the start of the controller and:

protected override void Dispose(bool disposing)
{
    db.Dispose();             
    base.Dispose(disposing);
}

at the end (and taking using out)

Although this works, I am curious as to why "Using" doesn't work, and I'm not really happy having the connection open throughout the controller, and disposing of it manually at the end.

Community
  • 1
  • 1
Ed Harrod
  • 3,423
  • 4
  • 32
  • 53
  • Have you tried with return outside using ? – User.Anonymous Apr 16 '14 at 13:22
  • at present I can't access the "topic" variable outside of the using statement – Ed Harrod Apr 16 '14 at 13:26
  • @ECH and you shouldn't be able to since it is scoped to the using statement. – Dismissile Apr 16 '14 at 13:37
  • @Dismissile Yes, I thought User.Anonymous was implying returning something else outside the using statement – Ed Harrod Apr 16 '14 at 13:40
  • Yes, I means with a solution as this : Topic topic = null; using { topic = ...} return topic; – User.Anonymous Apr 16 '14 at 13:59
  • duplicate of : http://stackoverflow.com/questions/18556924/disposing-database-context-with-using-statement-in-mvc – Vishal Sharma Apr 19 '14 at 05:51
  • You return `View(topic)` in some cases. It seems that this object tries to use the `DataContext` at some point after the above method has returned. But at that time the `DataContext` object has been disposed and cannot be used. This is what Dismissile describes in his answer. – Jeppe Stig Nielsen Apr 19 '14 at 06:29
  • @vishalsharma that question is referring to closing the using statement before return, doing this (and not doing this) resulted in the same error for this problem. – Ed Harrod Apr 22 '14 at 10:52

4 Answers4

7

Do any of your entities have lazy loading enabled? It seems like there are queries getting executed in your views but you are disposing your context before they get executed (hence the error saying it is already disposed). If you put the disposal in the controller Dispose method, the view will get executed before the controller and context is disposed.

I recommend installing Glimpse.Mvc5 and Glimpse.EF6 packages. Once you configure glimpse you can see every query that gets executed in your page. You might be surprised to see a few additional queries get executed that you did not intend. This is why I don't recommend using entities directly in your views.

Dismissile
  • 32,564
  • 38
  • 174
  • 263
  • Not sure why this was downvoted, to me it would seem like a very plausible explanation. – Joachim Isaksson Apr 16 '14 at 13:27
  • ,,,can you tell me the syntax of **using** please? i only know using for importing namespace like `using system;` – HackerMan Apr 16 '14 at 13:32
  • 1
    @HackerMan - if something implements IDisposable, you can put a using statement around it so that it calls dispose at the end of the scope. using(var db = new MyDbContext()) { } - at the end of the scope it will call Dispose() on db. – Dismissile Apr 16 '14 at 13:36
  • that means it calls the db dispose method to cleanup resources? – HackerMan Apr 16 '14 at 13:40
  • How does changing from a using statement to calling disposed manually at the end make a difference? To my view both techniques should dispose of the object before the view... – Chris Apr 16 '14 at 13:42
  • 1
    @Chris - nope. If you put the using in the Controller.Dispose method the result gets executed before the controller is disposed. If you put the using statement directly in the action method, it will get disposed as soon as the scope closes - which is before the result is executed. – Dismissile Apr 16 '14 at 13:45
  • Ah, I had misread it as calling dispose at the end of the action, not in the dispose method of controller. My bad. – Chris Apr 16 '14 at 13:46
3

This is happening because generally LINQ entities are proxy objects. If you have something like MyEntity.ChildEntities, the underlying SQL query isn't executed to fetch those objects until the code is executed. If you're accessing them in a view, the view isn't bound until after the action method returns, at which point the DbContext is already disposed.

The lifecycle looks something like this:

  1. Call action method
  2. The outer query to fetch the topic runs, but any additional accessors in the view that trigger more SQL queries are not executed yet.
  3. We've now left the action method, so using just disposed your DbContext.
  4. The MVC framework binds the model to the view, which triggers actually executing any remaining queries and fails since DbContext is disposed.
  5. The request lifecycle is about to end, so the controller is disposed.

Here's a good resource on lazy loading with entities.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • As I have been told, too, `FirstOrDefault` executes the query. – GSerg Apr 16 '14 at 13:28
  • FirstOrDefault executes the query. It doesn't matter if it is a proxy or not. Additional queries could be executed with lazy loading but FirstOrDefault will definitely execute the query. – Dismissile Apr 16 '14 at 13:29
  • @GSerg Yes, `FirstOrDefault` executes the query and returns a single Entity. Any _other_ linked (and not eagerly fetched) entities you access after the scope has ended will throw this error. – Joachim Isaksson Apr 16 '14 at 13:29
  • Yes, it does. That's what I get for skimming the question too quickly. – Rex M Apr 16 '14 at 13:29
  • 1
    The underlying problem is still the same - deferred execution. Just not the outer query. A few tweaks to the wording to more clearly describe this exact scenario. – Rex M Apr 16 '14 at 13:34
1

It was a problem with Lazy Loading, so thanks for the point in the right direction @Dismissile

Once I used '.Include' to load each virtual property of Topic, it worked fine:

var topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").Include("Forum").Include("DeletedBy").FirstOrDefault(x => x.Id == id);

I guess it would have been impossible for anyone to know which properties I had declared as virtual without me saying, sorry!

Incidentally, it worked before when I was inspecting the properties in debug mode, because each property would have to be loaded for me to inspect it

Ed Harrod
  • 3,423
  • 4
  • 32
  • 53
  • This is the same solution as I suggested !! i.e, Lazy loading problem and include() – stackunderflow Apr 16 '14 at 18:34
  • @ECH - if our solutions helped you then please mark one as an answer. – Dismissile Apr 21 '14 at 12:05
  • @stackunderflow your suggestion of "Use the Include() operator" was a little confusing, as I was already using it, but thanks anyway – Ed Harrod Apr 22 '14 at 10:40
  • Don't worry, I will try to write a fully detailed answer. Just I need time, sorry for any confusion – stackunderflow Apr 22 '14 at 11:01
  • 1
    Dear ECH, as i promised to write a full answer to your questions. I set up a project to answer this then I run into something that created a new question to me so I posted it in a new question that links here: http://stackoverflow.com/questions/23391909/deep-understanding-of-lazy-loading-and-disposing-error-in-mvc-net I hope the link will have good answer to your origional question, and sorry for any confusion that i caused :) – stackunderflow Apr 30 '14 at 16:51
0

You can't return view or something inside using context as this is causing problem disposing the context here.

correct way of using using statement in your case is

public ActionResult Topic(int id) //Topic Id
{
    Topic topic = null; // topic is your POCO
    using (var db = new DataContext())
    {
        topic = db.Topics.Include("Messages").Include("Messages.CreatedBy").Include("CreatedBy").FirstOrDefault(x => x.Id == id);   
    }
    return topic != null ? View(topic) : View();

}

also do not do response redirect if you in using statement..

see here : Disposing Database context with using statement in MVC

Community
  • 1
  • 1
Vishal Sharma
  • 2,773
  • 2
  • 24
  • 36