Lazy Loading vs Eager Loading in Entity Framework

Lazy Loading vs Eager Loading in Entity Framework

In this article, I am going to discuss Lazy Loading vs Eager Loading in Entity Framework with Examples. Please read our previous two articles where we discussed Eager Loading and Lazy Loading in Entity Framework. We are also going to work with the same application that we created in our Introduction to Database First Article, so please read our Introduction to Database First Approach article before proceeding to this article and get the required database scripts to create and populate the database as well as to know how we use the Entity Framework Database First Approach to create the Entity Data Model.

Eager Loading in Entity Framework:

As we already discussed, Eager loading is a Process where Entity Framework loads the related entities along with the main entity. That means, in this case, Entity Framework will not execute separate SQL queries for loading the related entities. So, all the entities are loaded from the database with a single query and hence saving bandwidth and server CPU time. Entity Framework use JOINs to load the related entities.

Lazy Loading in Entity Framework:

As we already discussed, 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. 

Lazy Loading vs Eager Loading in Entity Framework:

The one and only difference is loading the related entities. In Eager loading, all the related entities are loaded while loading the Main entity and on the other hand, with Lazy Loading, the related entities are going to be loaded on demand i.e. when required or when accessing the navigation property of the Main entity at that time only the related entities are going to be loaded from the database.

You can go with Eager Loading when you are sure that you will be using the related entities with the main entity everywhere. Eager Loading is a good practice to reduce the number of SQL queries to be sent to the database server to fetch the related entities. In the case of Eager Loading, it will use SQL Joining to join the related tables with the main table and then return the Main entity data along with the related entities.

You can go with Lazy Loading when you are sure that you are not using the Related Entities Instantly. Later at some point, you need some of the related entities and at that time, it will issue a separate SQL query to get the data. So, in this case, the number of round trips to the database is more as for each master entity data, it will issue a separate SQL query to get the child-related entity data.

When to Use Eager Loading and Lazy Loading in Entity Framework?

So, it completely depends on the business requirement of your application whether to use Lazy Loading or Eager Loading.

If you are not interested in related entities or the related entities are not used instantly, then you can use Lazy Loading to improve the application performance. In this case, if you go with Eager loading, then it will load the related entities instantly even though they are not required or are not required instantly. The reason is Eager Loading will generate and execute SQL Join whereas Lazy Loading simply uses the SELECT Statement without any join. So, definitely, in this case, Lazy Loading will give a better performance than Eager Loading.

If you are interested in related entities or the related entities are used instantly in your application, then you need to go with Eager Loading to get better performance. In this case, if you go with Lazy Loading, then it will not load the related entities instantly and it will load the related entities by issuing separate SQL queries. That means if the main entity has 100 records, then for each record i.e. 100 times it will issue a separate SQL Query to get the child entity data. Then you can imagine, how many round trips between the .NET Application and the Database. With Eager loading, while loading the Main entity the child entities are loaded. That means using a Single Round Trip i.e. using a single SQL Statement with Join will load the related entities. As the number of round trips is decreased, so the performance will be improved.

Example1 to Understand Lazy Loading and Eager Loading in Entity Framework:

So, what is our requirement is, we will load the student data along with it the course data. That means we need to display the student and their corresponding courses. Let us implement this with both Lazy Loading and Eager Loading and also try to observe the number of SQL statements generated and executed by the context object. Also, let us print the time taken by each approach to complete the task.

Example using Lazy Loading:

In the below example, we are using the Lazy Loading Approach to Load the Student and Courses Data. The following example is self-explained, so, please go through the comment lines.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Entity;

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 Entity as well as Related Entity Data
                context.Database.Log = Console.Write;

                //Create an Instance of the StopWatch, include System.Diagnostics namespace
                Stopwatch stopwatch = new Stopwatch();

                //Start the Stopwatch
                stopwatch.Start();

                //Loading the particular student data Using Lazy Loading
                //Here, it will only load the Student Data, no related entities
                List<Student> students = context.Students.ToList();
                foreach (var student in students)
                {
                    Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}");

                    //Here, It will Load the Related Courses Entitis by issuing Separate SQL Statement
                    foreach (var course in student.Courses)
                    {
                        Console.WriteLine($"CourseId: {course.CourseId} CourseName: {course.CourseName}");
                    }
                }
                
                //Stop the Stopwatch and print the time taken by Lazy Loading to do the task
                stopwatch.Stop();
                Console.WriteLine($"Lazy Loading Time Taken: {stopwatch.ElapsedMilliseconds}");
                Console.Read();
            }
        }
    }
}
Output:
Opened connection at 10-12-2022 20:57:57 +05:30
SELECT
    [Extent1].[StudentId] AS [StudentId],
    [Extent1].[FirstName] AS [FirstName],
    [Extent1].[LastName] AS [LastName],
    [Extent1].[StandardId] AS [StandardId]
    FROM [dbo].[Student] AS [Extent1]
-- Executing at 10-12-2022 20:57:57 +05:30
-- Completed in 9 ms with result: SqlDataReader

Closed connection at 10-12-2022 20:57:57 +05:30
Firstname: Virat, Lastname: Kohli
Opened connection at 10-12-2022 20:57:57 +05:30
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
-- EntityKeyValue1: '1' (Type = Int32, IsNullable = false)
-- Executing at 10-12-2022 20:57:57 +05:30
-- Completed in 4 ms with result: SqlDataReader

Closed connection at 10-12-2022 20:57:57 +05:30
CourseId: 1 CourseName: .NET
CourseId: 2 CourseName: Java
Firstname: Rohit, Lastname: Sharma
Opened connection at 10-12-2022 20:57:57 +05:30
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
-- EntityKeyValue1: '2' (Type = Int32, IsNullable = false)
-- Executing at 10-12-2022 20:57:57 +05:30
-- Completed in 0 ms with result: SqlDataReader

Closed connection at 10-12-2022 20:57:57 +05:30
CourseId: 3 CourseName: PHP
CourseId: 4 CourseName: Oracle
Firstname: KL, Lastname: Rahul
Opened connection at 10-12-2022 20:57:57 +05:30
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
-- EntityKeyValue1: '3' (Type = Int32, IsNullable = false)
-- Executing at 10-12-2022 20:57:57 +05:30
-- Completed in 0 ms with result: SqlDataReader

Closed connection at 10-12-2022 20:57:57 +05:30
CourseId: 1 CourseName: .NET
CourseId: 6 CourseName: Python
Firstname: Smriti, Lastname: Mandana
Opened connection at 10-12-2022 20:57:57 +05:30
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
-- EntityKeyValue1: '4' (Type = Int32, IsNullable = false)
-- Executing at 10-12-2022 20:57:57 +05:30
-- Completed in 0 ms with result: SqlDataReader

Closed connection at 10-12-2022 20:57:57 +05:30
CourseId: 5 CourseName: Android
CourseId: 6 CourseName: Python
Lazy Loading Time Taken: 1483

As you can see in the above output, it is generating and executing 5 SQL statements. One SQL Select to load the Main Entity data i.e. Student Entity Data and as Student Entity contains 4 records, so to load the related courses information, it generated and execute 4 separate SQL Statements. In my machine, it took around 1483 Milliseconds to complete the task. The time might be varied on your machine.

Example using Eager Loading:

Now, we will do the previous example using Eager Loading. As we already discussed to use Eager Loading, we need to use the Include Method of the DbSet and to this method, we need to specify the related property name. In the below example, we are using the Eager Loading Approach to Load the Student and Courses Data. The following example is self-explained, so, please go through the comment lines.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Entity;

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 Entity as well as Related Entity Data
                context.Database.Log = Console.Write;

                //Create an Instance of the StopWatch, include System.Diagnostics namespace
                Stopwatch stopwatch = new Stopwatch();

                //Start the Stopwatch
                stopwatch.Start();

                //Loading the particular student data along with its related data Using Eager Loading
                //Here, it will only load the Student Data, along with the Courses Data
                List<Student> students = context.Students.Include(std => std.Courses).ToList();
                foreach (var student in students)
                {
                    Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}");

                    //Already the Courses Entity Loaded, so no Separate SQL Statement by the context object
                    foreach (var course in student.Courses)
                    {
                        Console.WriteLine($"CourseId: {course.CourseId} CourseName: {course.CourseName}");
                    }
                }

                //Stop the Stopwatch and print the time taken by Lazy Loading to do the task
                stopwatch.Stop();
                Console.WriteLine($"Eager Loading Time Taken: {stopwatch.ElapsedMilliseconds}");
                Console.Read();
            }
        }
    }
}
Output:
Opened connection at 10-12-2022 21:05:15 +05:30
SELECT
    [Project1].[StudentId] AS [StudentId],
    [Project1].[FirstName] AS [FirstName],
    [Project1].[LastName] AS [LastName],
    [Project1].[StandardId] AS [StandardId],
    [Project1].[C1] AS [C1],
    [Project1].[CourseId] AS [CourseId],
    [Project1].[CourseName] AS [CourseName],
    [Project1].[TeacherId] AS [TeacherId]
    FROM ( SELECT
        [Extent1].[StudentId] AS [StudentId],
        [Extent1].[FirstName] AS [FirstName],
        [Extent1].[LastName] AS [LastName],
        [Extent1].[StandardId] AS [StandardId],
        [Join1].[CourseId1] AS [CourseId],
        [Join1].[CourseName] AS [CourseName],
        [Join1].[TeacherId] AS [TeacherId],
        CASE WHEN ([Join1].[StudentId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[Student] AS [Extent1]
        LEFT OUTER JOIN  (SELECT [Extent2].[StudentId] AS [StudentId], [Extent3].[CourseId] AS [CourseId1], [Extent3].[CourseName] AS [CourseName], [Extent3].[TeacherId] AS [TeacherId]
            FROM  [dbo].[StudentCourse] AS [Extent2]
            INNER JOIN [dbo].[Course] AS [Extent3] ON [Extent3].[CourseId] = [Extent2].[CourseId] ) AS [Join1] ON [Extent1].[StudentId] = [Join1].[StudentId]
    )  AS [Project1]
    ORDER BY [Project1].[StudentId] ASC, [Project1].[C1] ASC
-- Executing at 10-12-2022 21:05:15 +05:30
-- Completed in 14 ms with result: SqlDataReader

Closed connection at 10-12-2022 21:05:15 +05:30
Firstname: Virat, Lastname: Kohli
CourseId: 1 CourseName: .NET
CourseId: 2 CourseName: Java
Firstname: Rohit, Lastname: Sharma
CourseId: 3 CourseName: PHP
CourseId: 4 CourseName: Oracle
Firstname: KL, Lastname: Rahul
CourseId: 1 CourseName: .NET
CourseId: 6 CourseName: Python
Firstname: Smriti, Lastname: Mandana
CourseId: 5 CourseName: Android
CourseId: 6 CourseName: Python
Eager Loading Time Taken: 481

As you can see in the above output, it is generating and executing only one SQL Statement to load both Student and Course data. In my machine, it took around 481 Milliseconds to complete the task. The time might be varied on your machine.

Note: If you are getting reverse performance, please try to add more data to the Student and Courses table and then check the performance.

Example2 to Understand Lazy Loading and Eager Loading in Entity Framework:

So, what is our requirement is to display all the student data only. Let us implement this with both Lazy Loading and Eager Loading. Also, let us print the time taken by each approach to complete the task.

Example using Lazy Loading:

In the following example, we are using the Lazy Loading Approach to Load the Student Data. The following example is self-explained, so, please go through the comment lines.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Entity;

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 Entity as well as Related Entity Data
                context.Database.Log = Console.Write;

                //Create an Instance of the StopWatch, include System.Diagnostics namespace
                Stopwatch stopwatch = new Stopwatch();

                //Start the Stopwatch
                stopwatch.Start();

                //Loading the particular student data Using Lazy Loading
                //Here, it will only load the Student Data, no related entities
                List<Student> students = context.Students.ToList();
                foreach (var student in students)
                {
                    Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}");
                }

                //Stop the Stopwatch and print the time taken by Lazy Loading to do the task
                stopwatch.Stop();
                Console.WriteLine($"Lazy Loading Time Taken: {stopwatch.ElapsedMilliseconds}");
                Console.Read();
            }
        }
    }
}
Output:

Lazy Loading vs Eager Loading in Entity Framework

As you can see in the above output, it is using a single SELECT statement to load the student data and it is taking approximately 414 milliseconds on my machine.

Example using Eager Loading:

Now, we will do the previous example using Eager Loading. In the below example, we are using the Eager Loading Approach to Load the Student and related Courses Data even though Courses are not required. The following example is self-explained, so, please go through the comment lines.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data.Entity;

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 Entity as well as Related Entity Data
                context.Database.Log = Console.Write;

                //Create an Instance of the StopWatch, include System.Diagnostics namespace
                Stopwatch stopwatch = new Stopwatch();

                //Start the Stopwatch
                stopwatch.Start();

                //Loading the particular student data and its related data Using Eager Loading
                //Here, it will load the Student Data, along with the Courses Data
                List<Student> students = context.Students.Include(std => std.Courses).ToList();
                foreach (var student in students)
                {
                    Console.WriteLine($"Firstname: {student.FirstName}, Lastname: {student.LastName}");
                }

                //Stop the Stopwatch and print the time taken by Lazy Loading to do the task
                stopwatch.Stop();
                Console.WriteLine($"Eager Loading Time Taken: {stopwatch.ElapsedMilliseconds}");
                Console.Read();
            }
        }
    }
}
Output:

What is the difference between eager loading and lazy loading? Which is good - Eager Loading or Lazy Loading in Entity Framework?

As you can see in the above output, it is using a single SELECT statement using JOIN to load the student data and its related Courses data. It is taking approximately 460 milliseconds on my machine.

What is the difference between eager loading and lazy loading? Which is good – Eager Loading or Lazy Loading in Entity Framework?

Without looking at the application requirement and what we are trying to achieve, we cannot simply say one approach is better than the other approach. Both Lazy Loading and Eager Loading have their own advantages and disadvantages and we have seen, there are clear performance differences between these two approaches to achieve the same task.

With Eager Loading in Entity Framework, all the data i.e. main entity data, as well as its related entity data, is retrieved using a single query that involves SQL JOIN, which can be cached in memory to improve the application performance. So, with Eager Loading, we are reducing the number of round trips with the database.

With Lazy Loading in Entity Framework, we only retrieve just the amount of data that we need in a single query. When we need the related data, then additional queries are issued to the database. This means there are several round trips between the .NET Application and the database server. In general, these database round trips can degrade the application performance. Lesser the round trips, the better the performance.

So, the conclusion is that based on your application requirement, you need to choose whether you need to use Lazy Loading or Eager Loading in Entity Framework.

In the next article, I am going to discuss Explicit Loading in Entity Framework with Examples. In this article, I try to explain the Difference Between Eager Loading and Lazy Loading in Entity Framework with Examples and I hope you enjoyed this Difference Between Eager Loading and Lazy Loading in Entity Framework with Examples article. Please give your valuable feedback and suggestions about this article.

Registration Open For New Online Training

Enhance Your Professional Journey with Our Upcoming Live Session. For complete information on Registration, Course Details, Syllabus, and to get the Zoom Credentials to attend the free live Demo Sessions, please click on the below links.

Leave a Reply

Your email address will not be published. Required fields are marked *