I wanted to write up a short little entry about how the ObjectContext and Lazy-Loading works in situations where you typically lazy-load entities. In particular this is important to keep in mind when you maintain the context for longer period of times, or cache the context/entities in one way or another.
First off, the EF object context maintains the information about the entities it has loaded as long as the tracking option is enabled – by default this is enabled for EF4 with standard settings. Now, the only time these entities are accessed from the context rather than querying the database directly is when they are referred to from another related entity.
As an example of this, we can do (using the AdventureWorks DB)
This will result in 3 database queries as noted in the comments. It’s important to note that even though we already have the product with ID 680 stored in the context as a result of the first query, it still performs an additional query to retrieve the same object, as opposed to the lazy loaded entity which EF will be content to retrieve from the context once it’s in there.
This holds true regardless of where the information has been loaded from. Take the following example
Here we simply select all the ProductModel entites up front, which results in them being added to the object context even if they’re not “used” for anything.
This is all good when lazy-loading relatively static data. However, if the entities being lazy-loaded are changed through another context or in the database directly, this will potentially result in out-of-date data being used. In this case, if someone were to change the ProductModel relevant for our product, it wouldn’t refresh in our application as long as the object context lived, even if you “refresh” the main product. This can result in some unpredictable results and weird errors if you’re not aware of how it works.
If you find yourself in a scenario where you maintain the object context for a longer period of time, and think this could be a problem, there are a few ways to work around this. One way is to explicitly include the related entities in the query, another is to turn off entity tracking for the related objects altogether and you can also explicitly refresh the data if you’re using something similar to the .ToList() approach in the second picture.
This adds a bit of complexity to the SQL generated, as you tell EF to add a join clause to it, but will ensure that the ProductModule entity will be retrieved fresh from the database. If you have performance in mind for static and cached data, the option of simply loading all the relevant tables with .ToList() should be added as a contender if your includes are getting too complex.
Finally, the example of how the MergeOption can be used to ensure that we don’t get old data. It’s important to note that the option here is set on a table level, and it’s the table doing the queries which needs to have it set. If we set it on the ProductModel table, the Product table will still track its related ProductModel entities.
To summarize, make sure you know how the lazy loading works when you’re using it! Especially when using it in non-disposed contexts. If you rely on lazy-loading in such a context, make sure that you know that only explicit calls to the database will be fresh by default.