We are currently using lazy loading for Entity Framework and running into out of memory exception
. The reason why we're running into this exception is because the Linq query loads a lot of data and at latter stages it's using lazy loading to load navigation properties. But because we don't use NoTrackingChanges
Entity Framework cache builds up really quickly which results in out of memory error.
My understanding with EF is the we should always use NoTrackingChanges
on query unless you want to update the returned object from the query.
I then tested using NoChangeTracking
:
var account = _dbcontext.Account
.AsNoTracking()
.SingleOrDefault(m => m.id == 1);
var contactName = account.Contact.Name
but I get the following error:
System.InvalidOperationException: When an object is returned with a NoTracking merge option, Load can only be called when the EntityCollection or EntityReference does not contain objects.
Lazy loading means delaying the loading of related data, until you specifically request for it. When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook.
The AsNoTracking method can save both execution times and memory usage. Applying this option really becomes important when we retrieve a large amount of data from the database.
Entity Framework supports three ways to load related data - eager loading, lazy loading and explicit loading.
We can disable lazy loading for a particular entity or a context. To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.
You've specified for EF to not track your instantiated Account
value:
var account = _dbcontext.Account.AsNoTracking().SingleOrDefault(m=>m.id == 1);
Thus trying to access navigation properties off of them will never work:
var contactName = account.Contact.Name
You can explicitly include navigation properties you want by using the Include()
. So the following should work:
var account = _dbcontext.Account
.Include(a => a.Contact)
.AsNoTracking()
.SingleOrDefault(m=>m.id == 1);
var contactName = account.Contact.Name; // no exception, it's already loaded
I'm really not convinced that using AsNoTracking prevents from using lazy loading
It can be tested really quickly:
DotNetFiddle Full Example
public static void Main()
{
var actor1 = new Actor { Id = 1, Name = "Vin Diesel" };
var movie1 = new Movie { Id = 1, Title = "Fast and Furious", PrimaryActor = actor1 };
using (var context = new MovieDb())
{
Console.WriteLine("========= Start Add: movie1 ==============");
context.Movies.Add(movie1);
context.SaveChanges();
Console.WriteLine("========= END Add: movie1 ==============");
var m1 = context.Movies.First();
Console.WriteLine(m1.PrimaryActor.Name);
var m2 = context.Movies.Include(m => m.PrimaryActor).AsNoTracking().First();
Console.WriteLine(m2.PrimaryActor.Name);
var m3 = context.Movies.AsNoTracking().First();
Console.WriteLine(m3.PrimaryActor.Name);
}
}
Output:
========= Start Add: movie1 ==============
========= END Add: movie1 ==============
Vin Diesel
Vin Diesel
Run-time exception (line 31): Object reference not set to an instance of an object.
The variable m1
is tracked by the context, thus it can Lazy Load the navigation property and prints the value. m2
is not tracked, but I've explicitly included the navigation property so it prints the value. m3
is not tracked and I have not included it explicitly thus the value is null
and we get a NRE.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With