I am trying to populate a GridView
using Entity Frameworkm but every time I am getting the following error:
\"Property accessor \'LoanPro
By default Entity Framework uses lazy-loading for navigation properties. That's why these properties should be marked as virtual - EF creates proxy class for your entity and overrides navigation properties to allow lazy-loading. E.g. if you have this entity:
public class MemberLoan
{
public string LoandProviderCode { get; set; }
public virtual Membership Membership { get; set; }
}
Entity Framework will return proxy inherited from this entity and provide DbContext instance to this proxy in order to allow lazy loading of membership later:
public class MemberLoanProxy : MemberLoan
{
private CosisEntities db;
private int membershipId;
private Membership membership;
public override Membership Membership
{
get
{
if (membership == null)
membership = db.Memberships.Find(membershipId);
return membership;
}
set { membership = value; }
}
}
So, entity has instance of DbContext which was used for loading entity. That's your problem. You have using
block around CosisEntities usage. Which disposes context before entities are returned. When some code later tries to use lazy-loaded navigation property, it fails, because context is disposed at that moment.
To fix this behavior you can use eager loading of navigation properties which you will need later:
IQueryable<MemberLoan> query = db.MemberLoans.Include(m => m.Membership);
That will pre-load all memberships and lazy-loading will not be used. For details see Loading Related Entities article on MSDN.
In my case, I was passsing all models 'Users' to column and it wasn't mapped correctly, so I just passed 'Users.Name' and it fixed it.
var data = db.ApplicationTranceLogs
.Include(q=>q.Users)
.Include(q => q.LookupItems)
.Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users,*** ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data })
.ToList();
var data = db.ApplicationTranceLogs
.Include(q=>q.Users).Include(q => q.LookupItems)
.Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users.Name***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data })
.ToList();
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.
The InvalidOperationException
with this message always means the same thing: you are requesting data (entities) from entity-framework after the DbContext has been disposed.
(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.
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.
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.
Most of the other answers point to eager loading, but I found another solution.
In my case I had an EF object InventoryItem
with a collection of InvActivity
child objects.
class InventoryItem {
...
// EF code first declaration of a cross table relationship
public virtual List<InvActivity> ItemsActivity { get; set; }
public GetLatestActivity()
{
return ItemActivity?.OrderByDescending(x => x.DateEntered).SingleOrDefault();
}
...
}
And since I was pulling from the child object collection instead of a context query (with IQueryable
), the Include()
function was not available to implement eager loading. So instead my solution was to create a context from where I utilized GetLatestActivity()
and attach()
the returned object:
using (DBContext ctx = new DBContext())
{
var latestAct = _item.GetLatestActivity();
// attach the Entity object back to a usable database context
ctx.InventoryActivity.Attach(latestAct);
// your code that would make use of the latestAct's lazy loading
// ie latestAct.lazyLoadedChild.name = "foo";
}
Thus you aren't stuck with eager loading.
If you're using ASP.NET Core and wonder why you get this message in one of your async controller methods, make sure you return a Task
rather than void
- ASP.NET Core disposes injected contexts.
(I'm posting this answer as this question is high in the search results to that exception message and it's a subtle issue - maybe it's useful to people who Google for it.)
It's a very late answer but I resolved the issue turning off the lazy loading:
db.Configuration.LazyLoadingEnabled = false;