Reputation: 6676
I'm using EF6 with my web application, and just trying to shed light on how exactly eager loading / lazy loading works. From my research, it seems that if I want to have dynamic proxies, I would have to mark properties as "virtual". Also it seems that dynamic proxies are enabled by default. I have not disabled lazy loading or dynamic proxy loading in my db context. I have an example:
//action method which is passed in a projectId
var project = dbContext.Projects.SingleOrDefault(p => p.Id == projectId);
//inspect project by setting debug point here.
public class Project{
public Client Client{get;set;} // this has a dynamic proxy created when loading
public System System{get;set;} // this property is just null
}
Usually I have to get around this by going:
dbContext.Projects.Include(p => p.System).SingleOrDefault(p => p.Id == projectId);
What gives? Can someone demystify this for me?
Upvotes: 3
Views: 1439
Reputation: 34698
To ensure lazy loading proxies are enabled you need to declare the properties as virtual
and ensure that lazy loading hasn't been disabled on the DbContext.
The behaviour you are likely seeing is due to the dbContext already having fetched to one of the related entities and associating it automatically to your requested related entity.
Let's use an example with a Project (ID#1) with a Client (ID#1)
If you do something like:
using ( var context = new MyDbContext())
{
var project = context.Projects.Single(x => x.Id == 1);
Console.WriteLine("Has Client: " + (project.Client != null).ToString());
}
Without virtual
you would get "Has Client: False". With virtual
that console statement would trigger a 2nd query to the DB and then return "Has Client: True".
Now, where things get interesting:
using ( var context = new MyDbContext())
{
var tempClient = context.Clients.Single(x => x.Id == 1);
var project = context.Projects.Single(x => x.Id == 1);
Console.WriteLine("Has Client: " + (project.Client != null).ToString());
}
In this case our context simply loads a reference to Client ID #1. We don't do anything else or associate it to our project reference, we simply load the project same as before. In this case the output would be "Has Client: True", even though we don't eager load it, and it isn't marked as virtual
. Since Project #1 has a reference to Client #1 and Client #1 is already tracked by the DbContext, the reference is included when we request Project #1.
This is a consequence of using long running DbContexts that can lead to fairly unpredictable behaviour in applications that is completely situational. You need to take care with long running, even request bounded DbContext instances because this can result in getting incomplete pictures of your data. For instance if you have a parent with children references where Parent #1 has 3 children. (#1, #2, and #3) If your context for any reason has loaded and is tracking child #1 and #2, and you later load Parent #1 without eager loading the children, that parent's Children collection will just list 2 of the 3 children (#1 and #2) which can give your client an incomplete and inaccurate view of the data. (Fun bugs to track down when clients "sometimes" see incomplete data for no apparent reason.)
In general it is advisable to ensure all references are marked as virtual
and keep DbContext lifespans as short as possible. I recommend using a unit of work pattern which can be injected and lifetime scoped to the request, but responsible to create tighter lifetime scopes for the DbContexts themselves similar to using (var context = new MyDbContext())
without binding your code to the DbContext.
Upvotes: 2
Reputation: 12025
According to this tutorial, dynamic proxies are created iff:
ICollection <T>
. Are all those conditions met for your System
class?
Upvotes: 1