Back to: Entity Framework Tutorials For Begineers and Professionals
Lazy Loading in Entity Framework with Examples
In this article, I am going to discuss Lazy Loading in Entity Framework Database First Approach with Examples. Please read our previous article where we discussed Eager Loading in Entity Framework with Examples. At the end of this article, you will understand what is Lazy Loading and how to implement Lazy Loading in Entity Framework. You will also learn how to disable Lazy Loading for a particular and for all entities and finally, we will discuss when to use Lazy Loading in Entity Framework. We are going to work with the same application that we created in our Introduction to Entity Framework Database First article. Please read our introduction to Entity Framework Database First article before proceeding to this article.
Lazy Loading in Entity Framework:
Lazy Loading is a Process where Entity Framework loads the related entities on demand. Lazy Loading is the default behavior of Entity Framework. That means the related entities or child entities are loaded only when it is being accessed for the first time. That means in simple words we can say that Lazy loading simply delays the loading of related entities until you specifically request it.
How to Implement Lazy Loading in Entity Framework?
For Lazy Loading of Related Entities in Entity Framework, we don’t need to do anything special and the reason is by default Entity Framework uses Lazy Loading to load the related entities.
Let us understand this with an example. Please have a look at the following Student entity which is auto-generated by Entity Framework. If you look at the Student class, then you will find a navigation property for the StudentAddress entity. At the same time, you will also find a navigation property for Standard and a collection navigation property for Courses Entities.
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace DBFirstApproach { using System; using System.Collections.Generic; public partial class Student { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public Student() { this.Courses = new HashSet<Course>(); } public int StudentId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable<int> StandardId { get; set; } public virtual Standard Standard { get; set; } public virtual StudentAddress StudentAddress { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection<Course> Courses { get; set; } } }
In Lazy Loading, the context object first loads the Student entity data from the database, then it will load the related entities when you access those navigation properties. If you are not accessing the Navigation Properties, then it will not load those related data from the database.
For example, when we execute the below statement, the context object will only load the Student table data by using a SELECT SQL Statement. It will not load the related StudentAddress, Standard, or Courses table data.
Student student = context.Students.FirstOrDefault(std => std.StudentId == 1);
Later, if you execute the below statement, that is when we access the Student Address Property for the First Time, the Context object will issue a SELECT SQL Statement and load the StudentAddress data from the database table.
StudentAddress studentAddress = student.StudentAddress;
Similarly, when you execute the below statement, the Context object will generate and execute another SQL SELECT Statement to fetch the data from the database table and load the related data.
Standard standard = student.Standard;
Example to understand Lazy Loading in Entity Framework:
Whatever we have discussed so far is put together in the below example code. The following example will use Lazy Loading to load the related Student Address, Standard, and Course information of the Student by executing separate SQL Statements. The following example code is self-explained, so please go through the comment lines.
using System; using System.Linq; namespace DBFirstApproach { class Program { static void Main(string[] args) { using (EF_Demo_DBEntities context = new EF_Demo_DBEntities()) { //To See the SQL Generated to Load the Main as well as Related Entity Data context.Database.Log = Console.Write; //Loading the particular student data only //Here, it will only load the Student Data, no related entities Student student = context.Students.FirstOrDefault(std => std.StudentId == 1); Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}"); Console.WriteLine(); //Loading the Student Address (it will execute separate SQL query) StudentAddress studentAddress = student.StudentAddress; Console.WriteLine($"AddresLin1 {studentAddress.Address1}, AddresLin2 {studentAddress.Address2}"); Console.WriteLine(); //Loading the Standard (it will execute separate SQL query) Standard standard = student.Standard; Console.WriteLine($"StandardName: {standard.StandardName}, Description: {standard.Description}"); Console.WriteLine(); //Loading the Course (it will execute separate SQL query) var courses = student.Courses; foreach (var course in courses) { Console.WriteLine($"CourseName: {course.CourseName}"); } Console.Read(); } } } }
The code shown in the above example will result in four SQL queries.
First, it will fetch the particular student data by executing the below SQL Query when the (Student student = context.Students.FirstOrDefault(std => std.StudentId == 1);) statement is executed:
SELECT TOP (1) [Extent1].[StudentId] AS [StudentId], [Extent1].[FirstName] AS [FirstName], [Extent1].[LastName] AS [LastName], [Extent1].[StandardId] AS [StandardId] FROM [dbo].[Student] AS [Extent1] WHERE 1 = [Extent1].[StudentId]
When (StudentAddress studentAddress = student.StudentAddress;) statement is executed, it will generate and execute the following SQL query:
exec sp_executesql N'SELECT [Extent1].[StudentId] AS [StudentId], [Extent1].[Address1] AS [Address1], [Extent1].[Address2] AS [Address2], [Extent1].[Mobile] AS [Mobile], [Extent1].[Email] AS [Email] FROM [dbo].[StudentAddress] AS [Extent1] WHERE [Extent1].[StudentId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
When (Standard standard = student.Standard;) statement is executed, it will generate and execute the following SQL query in the database:
exec sp_executesql N'SELECT [Extent1].[StandardId] AS [StandardId], [Extent1].[StandardName] AS [StandardName], [Extent1].[Description] AS [Description] FROM [dbo].[Standard] AS [Extent1] WHERE [Extent1].[StandardId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
When (var courses = student.Courses;) statement is executed, it will generate and execute the following SQL query in the database:
exec sp_executesql N'SELECT [Extent2].[CourseId] AS [CourseId], [Extent2].[CourseName] AS [CourseName], [Extent2].[TeacherId] AS [TeacherId] FROM [dbo].[StudentCourse] AS [Extent1] INNER JOIN [dbo].[Course] AS [Extent2] ON [Extent1].[CourseId] = [Extent2].[CourseId] WHERE [Extent1].[StudentId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
How to Disable Lazy Loading in Entity Framework?
We can disable lazy loading for a particular entity and also for all entities by disabling it at the context (for all entities).
Disabling Lazy Loading for a Particular Entity in Entity Framework:
To turn off Lazy Loading for a particular entity, do not make it virtual. If you see, by default Entity Framework creates the Model classes as Partial and the Navigation Properties as Virtual. So, we just need to remove the Virtual keyword from the property to disable Lazy Loading. For example, if you want to disable Lazy Loading for StudentAddress or Standard entities while loading the Student Entity, then you need to go to the auto-generated student entity class and you need to make the StudentAddress or Standard property as non-virtual by removing the virtual keyword as shown in the below image.
Please Modify the Auto-Generated Student entity as shown below to disable Lazy Loading for Standard and StudentAddress properties while loading the Student data from the database. But still, Lazy Loading is Enabled for Courses data.
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace DBFirstApproach { using System; using System.Collections.Generic; public partial class Student { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public Student() { this.Courses = new HashSet<Course>(); } public int StudentId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable<int> StandardId { get; set; } public Standard Standard { get; set; } public StudentAddress StudentAddress { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection<Course> Courses { get; set; } } }
With the above changes in place, now Lazy loading is disabled for the StudentAddress and Standard properties. Now it will not load the StudentAddress property when you call the StudentAddress navigation property as well as it will not load the Standard property when you call the Standard navigation property of the student object as shown in the below example. But, the Courses data will be loaded when you call the Courses Navigation property as Lazy Loading is not disabled for this property.
using System; using System.Linq; namespace DBFirstApproach { class Program { static void Main(string[] args) { using (EF_Demo_DBEntities context = new EF_Demo_DBEntities()) { //To See the SQL Generated to Load the Main as well as Related Entity Data context.Database.Log = Console.Write; //Loading the particular student data only //Here, it will only load the Student Data, no related entities Student student = context.Students.FirstOrDefault(std => std.StudentId == 1); Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}"); //It will not laod the StudentAddress data as Lazy Loading is disabled StudentAddress studentAddress = student.StudentAddress; if (studentAddress == null) { Console.WriteLine("Student Address id not loaded"); } else { Console.WriteLine($"AddresLin1 {studentAddress.Address1}, AddresLin2 {studentAddress.Address2}"); } //It will not laod the Standard data as Lazy Loading is disabled Standard standard = student.Standard; if (standard == null) { Console.WriteLine("Standard is Not Loaded"); } else { Console.WriteLine($"StandardName: {standard.StandardName}, Description: {standard.Description}"); } //Loading the Course (it will execute separate SQL query) as Lazy Loading is Enabled for Courses var courses = student.Courses; foreach (var course in courses) { Console.WriteLine($"CourseName: {course.CourseName}"); } Console.Read(); } } } }
Now, if you run the above code, then you will get the following output. Here, you can see, for loading the Main entity which is Student Entity, it is generating and executing one SELECT SQL Statement. As we have disabled Lazy Loading for StudentAddress and Standard properties, even though we are accessing these two properties, no SQL Query is generated and executed by the context object and hence it is printing the same that is Student Address and Standard is not loaded. Finally, you can see, while accessing the Courses property, it is generating and executing one SQL Query to load the data and this is because, by default, Lazy Loading is Enabled for the Courses Navigation property of Student Entity.
Before proceeding further and understanding how to disable Lazy Loading for all Entities, let us revert to the change that we have done in the Student Entity. That is making the StudentAddress and Standard properties as virtual as follows:
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace DBFirstApproach { using System; using System.Collections.Generic; public partial class Student { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public Student() { this.Courses = new HashSet<Course>(); } public int StudentId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable<int> StandardId { get; set; } public virtual Standard Standard { get; set; } public virtual StudentAddress StudentAddress { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection<Course> Courses { get; set; } } }
How to Disable Lazy Loading for all Entities in Entity Framework:
Disabling Lazy Loading for All Entities in Entity Framework is very simple and Straight forward. What we need to do is, we need to go to the Context class and within the context class constructor, we need to set the LazyLoadingEnabled Flag of the Configuration Property to False as shown in the below image. The LazyLoadingEnabled flag value by default is TRUE which means Lazy Loading is enabled for all entities by default and once we set the LazyLoadingEnabled Flag value to false, then Lazy Loading is disabled for all Entities that are defined inside this context class.
With the above changes in place, now Lazy loading is disabled for all the entities of our EF_Demo_DBEntities context class. Now, modify the Main method of the Program class as follows.
using System; using System.Linq; namespace DBFirstApproach { class Program { static void Main(string[] args) { using (EF_Demo_DBEntities context = new EF_Demo_DBEntities()) { //To See the SQL Generated to Load the Main as well as Related Entity Data context.Database.Log = Console.Write; //Loading the particular student data only //Here, it will only load the Student Data, no related entities Student student = context.Students.FirstOrDefault(std => std.StudentId == 1); Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}"); //It will not laod the StudentAddress data as Lazy Loading is disabled StudentAddress studentAddress = student.StudentAddress; if (studentAddress == null) { Console.WriteLine("Student Address id not loaded"); } else { Console.WriteLine($"AddresLin1 {studentAddress.Address1}, AddresLin2 {studentAddress.Address2}"); } //It will not laod the Standard data as Lazy Loading is disabled Standard standard = student.Standard; if (standard == null) { Console.WriteLine("Standard is Not Loaded"); } else { Console.WriteLine($"StandardName: {standard.StandardName}, Description: {standard.Description}"); } //Loading the Course (it will execute separate SQL query) as Lazy Loading is Enabled for Courses var courses = student.Courses; if(courses != null) { foreach (var course in courses) { Console.WriteLine($"CourseName: {course.CourseName}"); } } { Console.WriteLine("Courses is Not Loaded"); } Console.Read(); } } } }
Now, run the above code, and you will get the following output. As you can see, it is only generating and executing a single SQL Statement to load the Student Entity and as Lazy Loading is disabled, so no SQL Query is generated and executed when we access the Navigation properties of the Student entity to load the related entities data.
Note: Please remove the context.Configuration.LazyLoadingEnabled = false statement from the context class.
Rules for Lazy Loading in Entity Framework:
- The context.Configuration.ProxyCreationEnabled property should be true. By default, it is set to TRUE.
- The context.Configuration.LazyLoadingEnabled property should be true. By default, it is set to TRUE.
- The Navigation property should be defined as public and virtual. The Context object will not do lazy loading if the property is not defined as virtual.
When to use Lazy Loading in Entity Framework?
- You need to use Lazy Loading when you are sure that you are not using Related Entities Instantly.
In the next article, I am going to discuss Lazy Loading vs Eager Loading in Entity Framework with Examples. In this article, I try to explain Lazy Loading in Entity Framework with Examples and I hope you enjoyed this Lazy Loading in Entity Framework with Examples article. Please give your valuable feedback and suggestions about this article.
Very good explanation.
Very helpful thank you so much