Lazy loading in Entity Framework is the technique where it delays the loading of an entity or collection of entities until the time application actually needs it. In this tutorial, we learn about lazy loading. The difference between lazy loading and eager loading. How it works. How to disable it for all entities, for a specific entity or to a specific query.
Table of Contents
Lazy Loading in Entity Framework
Entity Framework by default enables the lazy loading. Our initial query only retrieves the main entity. The related entry is loaded only when we access a navigation property.
The Product
& ProductModel
has a one to Many Relationship between them. We can navigate to the ProductModel
from the Product
using the navigation property.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void LazyLoading() { using (AdventureWorks db = new AdventureWorks()) { db.Database.Log = Console.Write; var product = (from p in db.Products where p.ProductID == 814 select p).ToList(); foreach (var p in product) { Console.WriteLine("{0} {1} {2}", p.ProductID, p.Name, p.ProductModel.Name); } Console.ReadLine(); } } |
The Code above retrieves the Product
from the database when the ToList()
method is called. The Entity framework does not retrieve the ProductModel
at that time. We access the name of the ProductModel
inside for the loop. At that point, Entity Framework sends the query to the database to retrieve the ProductModel
.
How does Lazy loading work
The Entity Framework often creates a Proxy
class derived from the entity. It creates these classes dynamically. They act as a proxy to the entity from which they are derived.
These Proxy
classes override the virtual
navigation properties of your POCO class and insert additional code to retrieve the data from the database when the navigation property is accessed. This mechanism helps to support the lazy loading of related data.
Proxy classes are only created if the navigational property is marked as Public
& Virtual
. If you open Product
class, then you can see that the ProductModel
is a Virtual
Property
1 2 3 | public virtual ProductModel ProductModel { get; set; } |
Lazy Loading & Serialization
Serialization and Lazy loading do not work well. Especially when you have properties that hold a reference to each other. For example, consider the following model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public partial class Product { public int ProductID { get; set; } public string Name { get; set; } public virtual ProductModel ProductModel { get; set; } } public partial class ProductModel { public int ProductModelID { get; set; } public string Name { get; set; } public virtual ICollection<Product> Products { get; set; } } |
When you try to serialize the Product
instance.
- The serializer starts to serialize the
Product
. - It reads the
ProductModel
property. The EF will send a query to the database and update theProductModel
- Serializer attempt to serialize the
ProductModel
. - The
ProductModel
contains the collectionProducts
. The EF Lazy loads theProducts
from the database. - Now Serializer attempts to read the each
Product
in theProducts
collection. - Each of those
product
hasProductModel
and theProductModel
hasProducts
collection - The Loop goes on.
The solution is either to disable Lazy Loading or create another class and map the Product
to an instance of that class and use it in Serialization.
Lazy Loading vs. Eager Loading
Entity Framework by default enables the lazy loading. It can create a lot of redundant round trip database. For Example in the following example, as we touch the p.ProductModel.Name
EF will send a query to the database server. The number of trips is equal to the number of products
and if that is in thousands, you have a serious performance issue. Such queries sometimes are difficult spot and the better option is to disable the Lazy Loading.
You can easily solve it by eager loading (i,e using the include
method). The eager loading gets all the data at one go, thus reducing the round trip required.
1 2 3 4 5 6 7 | var product = (from p in db.Products select p).ToList(); foreach (var p in product) { Console.WriteLine("{0} {1} {2}", p.ProductID, p.Name, p.ProductModel.Name); } |
Disable Lazy Loading
The lazy loading is the default behavior in Entity Framework. But you can turn it off you wish to. You can disable the Lazy Loading for the entire context, for a selected Navigation Property or for a selected query.
Remove the virtual
keyword from the property as shown below. Now if you run our example code, you will get Object reference not set to an instance of an object. Error
.
1 2 3 | public ProductModel ProductModel { get; set; } |
Disable it for a Specific Query
Make the Configuration.LazyLoadingEnabled
of the DbContext
to false
as shown below. This disables the Lazy Loading until the context
is disposed.
1 2 3 4 5 6 7 | using (AdventureWorks db = new AdventureWorks()) { db.Database.Log = Console.Write; db.Configuration.LazyLoadingEnabled = false; } |
Disable It for All Entities
You can turn off the lazy loading of all entities by making LazyLoadingEnabled
property to false
in the constructor of the DbContext
class as shown below.
1 2 3 4 5 6 | public AdventureWorks() : base("name=AdventureWorks") { this.Configuration.LazyLoadingEnabled = false; } |
You can also disable the lazy loading by making ProxyCreationEnabled
property to false
. This Property actually disables the creation of Dynamic Proxy classes. Since Lazy loading requires Proxy classes, disabling the Proxy Creation also disables the Lazy Loading
1 2 3 4 5 6 | public AdventureWorks() : base("name=AdventureWorks") { this.Configuration.ProxyCreationEnabled = false; } |
Why would someone want to turn off Lazy Loading?
Because no one wants to have SQL N+1 issue. Lazy Loading should be illegal.