Engin Ozsozgun
Engin Ozsozgun

Reputation: 47

How can I fix "The ObjectContext instance has been disposed "

I developed an application with entity framework. I get the

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

error from sometimes.

I did some research on the internet but I couldn't figure it out. I would be very grateful if you could help

HomeController:

public ActionResult Index()
{
    return View(noteManager.ListQueryable().Where(x => x.IsDraft == false && x.IsApproved == true).OrderByDescending(x => x.ModifiedOn).Take(10).ToList());
}

My Note Entity:

public class Note : MyEntitesBase
{       
    public string Tittle { get; set; }
    public string Text { get; set; }
    public bool IsDraft { get; set; }
    public int LikeCount { get; set; }
    public int CategoryID { get; set; }

    public virtual EvernoteUser Owner { get; set; } 
    public virtual List<Comment> Comments { get; set; } 
    public virtual Category Category { get; set; } 
    public virtual List<Liked> Likes { get; set; } 

    public Note()
    {
        Comments = new List<Comment>();
        Likes = new List<Liked>();
    }

}

My Comment Entity:

public class Comment : MyEntitesBase
{
    public string Text { get; set; }
    public bool CommentStatus { get; set; }

    public virtual Note Note { get; set; }
    public virtual EvernoteUser Owner { get; set; } 
}

My DatabaseContext:

public class DatabaseContext :DbContext 
{
   public DbSet<EvernoteUser> EvernoteUsers { get; set; }
   public DbSet<Note> Notes { get; set; }
   public DbSet<Comment> Comments { get; set; }
   public DbSet<Category> Categories { get; set; }
   public DbSet<Liked> Likes { get; set; }

   public DatabaseContext()
   {           
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<DatabaseContext,Configuration>()); 

    }

}

Upvotes: 0

Views: 2765

Answers (1)

Steve Py
Steve Py

Reputation: 34653

This issue often stems from the serializer which will be touching all properties on the entity being serialized to send to the view. If the DbContext the entity was associated with gets disposed, the serializer will hit this when attempting to issue queries to load the related details.

The quick fix, if noteManager.ListQueryable() returns IQueryable<Note> would be:

return View(noteManager.ListQueryable()
    .Include(x => x.Owner)
    .Include(x => x.Comments)
    .Include(x => x.Category)
    .Include(x => x.Likes)
    .Where(x => x.IsDraft == false && x.IsApproved == true)
    .OrderByDescending(x => x.ModifiedOn)
    .Take(10).ToList());

This eager loads the related entities along with the notes. The difference between eager loading and lazy loading is that with an eager load, EF will generate SQL to Join all of the associated tables then retrieve the related rows for the up to 10 selected rows. With lazy loading you might have 10 note rows, ID's 1-10 for instance, but as each property is touched, EF will generate a queries like:

SELECT * FROM Owners WHERE OwnerID = 22 -- Owner ID on note 1

SELECT * FROM Comments WHERE NoteId = 1

SELECT * FROM Categories WHERE CategoryId = 4 Category ID on note 1

SELECT * FROM Likes WHERE NoteId = 1

Then repeat that 9 more times, once for each note row returned. That is a lot of queries that EF and the DB need to negotiate while the entity's proxy is holding a weak reference to the DbContext. If the request disposes of the DbContext before the serializer finishes with the entity, you're handed a steaming Exception.

However, Even with eager loading this can be a rabbit hole if any of those related entities have child entities themselves. There is also the performance/resource implications of loading all of that related data that your view probably won't need.

The better long term solution is to define serializable ViewModels to represent the data structure your view actually needs to display, then leverage Select or Automapper's ProjectTo to fill that view model with data from the entity structure. This negates the need to eager load data, just Select from the structure and EF will work out the SQL. It also removes the risk of lazy load hits in the serializer provided you Select fields from the entities and not the entities themselves. This also can greatly reduce the amount of memory needed on the server & client to store the data when requested, and the data transmission size.

Passing view models to the view means passing the same, or different view models back to the server rather than trying to pass back entities, attach, and save... Which looks like more work and a time saving compared to loading the data again and copying the values across. However, this is much safer as you don't risk stale, incomplete, or potentially tampered data overwriting your real data. You should always reload the entities when performing an update anyways to validate and check that the row(s) have not been modified since they were sent to the client. Trust nothing that comes from a web client. Copying fields to a freshly loaded entity also means more efficient UPDATE statements as Attaching + EntityState.Modified or using DbContext.Update() result in update statements that update all fields, vs with copy across, only the values that are changed will be added to the UPDATE statement.

Upvotes: 1

Related Questions