Solving “The ObjectContext instance has been disposed and can no longer be used for operations that require a connection” InvalidOperationException

前端 未结 7 2221
再見小時候
再見小時候 2020-11-22 06:38

I am trying to populate a GridView using Entity Frameworkm but every time I am getting the following error:

\"Property accessor \'LoanPro

7条回答
  •  梦如初夏
    2020-11-22 06:49

    Bottom Line

    Your code has retrieved data (entities) via entity-framework with lazy-loading enabled and after the DbContext has been disposed, your code is referencing properties (related/relationship/navigation entities) that was not explicitly requested.

    More Specifically

    The InvalidOperationException with this message always means the same thing: you are requesting data (entities) from entity-framework after the DbContext has been disposed.

    A simple case:

    (these classes will be used for all examples in this answer, and assume all navigation properties have been configured correctly and have associated tables in the database)

    public class Person
    {
      public int Id { get; set; }
      public string name { get; set; }
      public int? PetId { get; set; }
      public Pet Pet { get; set; }
    }
    
    public class Pet 
    {
      public string name { get; set; }
    }
    
    using (var db = new dbContext())
    {
      var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
    }
    
    Console.WriteLine(person.Pet.Name);
    

    The last line will throw the InvalidOperationException because the dbContext has not disabled lazy-loading and the code is accessing the Pet navigation property after the Context has been disposed by the using statement.

    Debugging

    How do you find the source of this exception? Apart from looking at the exception itself, which will be thrown exactly at the location where it occurs, the general rules of debugging in Visual Studio apply: place strategic breakpoints and inspect your variables, either by hovering the mouse over their names, opening a (Quick)Watch window or using the various debugging panels like Locals and Autos.

    If you want to find out where the reference is or isn't set, right-click its name and select "Find All References". You can then place a breakpoint at every location that requests data, and run your program with the debugger attached. Every time the debugger breaks on such a breakpoint, you need to determine whether your navigation property should have been populated or if the data requested is necessary.

    Ways to Avoid

    Disable Lazy-Loading

    public class MyDbContext : DbContext
    {
      public MyDbContext()
      {
        this.Configuration.LazyLoadingEnabled = false;
      }
    }
    

    Pros: Instead of throwing the InvalidOperationException the property will be null. Accessing properties of null or attempting to change the properties of this property will throw a NullReferenceException.

    How to explicitly request the object when needed:

    using (var db = new dbContext())
    {
      var person = db.Persons
        .Include(p => p.Pet)
        .FirstOrDefaultAsync(p => p.id == 1);
    }
    Console.WriteLine(person.Pet.Name);  // No Exception Thrown
    

    In the previous example, Entity Framework will materialize the Pet in addition to the Person. This can be advantageous because it’s a single call the the database. (However, there can also be huge performance problems depending on the number of returned results and the number of navigation properties requested, in this instance, there would be no performance penalty because both instances are only a single record and a single join).

    or

    using (var db = new dbContext())
    {
      var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
    
      var pet = db.Pets.FirstOrDefaultAsync(p => p.id == person.PetId);
    }
    Console.WriteLine(person.Pet.Name);  // No Exception Thrown
    

    In the previous example, Entity Framework will materialize the Pet independently of the Person by making an additional call to the database. By default, Entity Framework tracks objects it has retrieved from the database and if it finds navigation properties that match it will auto-magically populate these entities. In this instance because the PetId on the Person object matches the Pet.Id, Entity Framework will assign the Person.Pet to the Pet value retrieved, before the value is assigned to the pet variable.

    I always recommend this approach as it forces programmers to understand when and how code is request data via Entity Framework. When code throws a null reference exception on a property of an entity, you can almost always be sure you have not explicitly requested that data.

提交回复
热议问题