Lazy Loading with EF Core

Using EF Core, references can be eager loaded, explicitly loaded, and lazy loaded. With eager loading you load references by specifying what references should be included when defining the query. Specifying the query, you use the Include method to define what references should be included. This is best when knowing in advance the needed references. With explicit loading you explicitly invoke the Load method when references to single items or collections should be loaded. With lazy loading, you do not need to explicitly invoke the Load method. Instead, just accessing the property dynamically invokes a query to the database to retrieve the data for the needed references.

With EF Core, lazy loading is available since version 2.1. This article gives information on lazy loading which is very different to lazy loading with Entity Framework 6.



I usually prefer eager loading or explicit loading to lazy loading. With lazy loading, reading the code you can easily miss where queries to the database are done. More queries than needed could be done to retrieve data from the database. Using eager loading you can reduce the number of queries, and access relations with a single SQL query. Using explicit loading, usually the same number of queries like with lazy loading are done – it’s just shown explicitly where the database access is used.

Using lazy loading, every time a property is accessed and the data is not yet retrieved, a query to the database is done.

Because of the disadvantages of lazy loading, comparing the implementations of lazy loading between Entity Framework and EF Core, it’s now implemented in a different way. You need to explicitly turn it on.

If the issues don’t apply to your scenario, go ahead and use lazy loading. There’s just a little more work needed defining the model types, and doing some configuration.


To use lazy loading, an additional package needs to be added. Lazy loading is not available if you just use the package Microsoft.EntityFrameworkCore. For using lazy loading, the package Microsoft.EntityFrameworkCore.Proxies needs to be added to the project references. As mentioned previously, because of the lazy loading disadvantages, it’s not included by default with your EF Core projects.

Checking the dependencies, Microsoft.EntityFrameworkCore.Proxies is dependent on the package Castle.Core. Castle.Core is part of the Castle Project. The Castle Project consists of several interesting projects. One of these is DynamicProxy. EF Core makes use of this package for dynamic-loading proxies.

With the sample code I’m using Visual Studio 2019 and C# 8. In case you don’t have this versions yet, you can change the code easily to use C# 7 – just remove some features on nullable reference types.

Configure Proxy

Proxies are used to make lazy loading possible. After adding the reference to the NuGet package, proxies can be configured using the method UseLazyLoadingProxies. This is an extension method to extend the DbContextOptionsBuilder. This method turns on proxy creation and uses proxy services implemented in the referenced NuGet package.

With the sample code, the lazy loading proxies are configured using the DI container. In case you are not using the DI Container with your EF Core application, you can use the method UseLazyLoadingProxies in the OnConfiguring method of the EF Core context.

Models and Context

The sample application defines the model classes Book, Chapter, and User.

The Book class contains a list of chapters with a one-to-many relation. The Book type also references multiple User types with different roles. A user can be the author, the reviewer, or the project editor. To make the class ready for lazy loading, the relations need to be specified with the virtual modifier. Behind the scenes, the created proxy class derives from the Book class and overrides these properties. The proxy then loads the data needed on first access of the property.

With the EF Core context class BooksContext, the fluent API is used to specify the relations. The book has a list of chapters, and a chapter belongs to one book – this is specified with HasMany and WithOne. One user is associated with multiple books with the relations WrittenBooks, ReviewedBooks, and EditedBooks.

The sample code uses the fluent API to specify the relation. Relations can also be specified using annotations. The book Professional C# 7 and .NET Core 2.0 covers all variants.

Lazy Loading

To access books, LINQ queries can be done like the one shown passing a where clause. After iterating the books, the references to chapters, authors, reviewers, and editors are done just by accessing the properties of the Book type:

Accessing the books, from the LINQ statement a SQL query is generated to access different columns from the Books table:

SELECT [b].[BookId], [b].[AuthorId], [b].[EditorId], [b].[Publisher], [b].[ReviewerId], [b].[Title]
      FROM [Books] AS [b]
      WHERE [b].[Publisher] LIKE N'Wrox' + N'%' AND (LEFT([b].[Publisher], LEN(N'Wrox')) = N'Wrox')

With the first query, other tables than the Books table are not accessed. However, accessing the Chapters property, this SQL query is done:

SELECT [e].[ChapterId], [e].[BookId], [e].[Number], [e].[Title]
      FROM [Chapters] AS [e]
      WHERE [e].[BookId] = @__get_Item_0

Later on in the code, accessing the Author, Reviewer, and Editor relations, more queries are done.

SELECT [e].[UserId], [e].[Name]
      FROM [Users] AS [e]
      WHERE [e].[UserId] = @__get_Item_0

When the data was not loaded previously, every time a property is accessed that maps to a related table, another query to the database is done. Behind the scenes, the query doesn’t return the defined Book types, but instead the class Castle.Proxies.BookProxy is returned. This class derives from the base class Book and overrides virtual properties.

Explicit Loading

With lazy loading, just properties need to be accessed from the C# code to get the data from the database as needed. This is easy to do, but you might miss better performance by reducing the number of queries. Using explicit loading you do have the same number of queries, it’s just easier to detect from the source code. Using the Collection method from the EntityEntry type, 1:n relations can be retrieved from the program invoking the Load method. Here, the same SQL statement is generated accessing the book chapters as with lazy loading. Accessing a 1:1 relation, the Reference method is used – again with the Load method.

You can also use the IsLoaded property to see if the related data is already loaded. The implementation of the Load method itself checks if the related data is already loaded to not query the database another time if the data is already in memory.

With explicit loading the source code gets more complex when accessing the objects from the EF Core database context. Related data needs to be explicit loaded using the Load method from the CollectionEntry or the ReferenceEntry returned from the Collection and Reference methods. The advantage using explicit loading is that you see it from the C# source code that additional SQL queries are done. Also, the model type doesn’t need special treatment. Here, virtual properties are no longer needed.

Eager Loading

In case you already know in advance the needed loaded relations, eager loading can be used. This should be the preferred way to get the data from the database. You can get deep relations with just one query. Defining the LINQ query, you now add calls to the Include method and specify the relations that should be included. The Include method is an extension to the IQueryable type defined in the Microsoft.EntityFrameworkCorenamespace.

In case you have deeper relations, and need to access relation by relation, e.g. by accessing another relation from the Chapter type, you can use the ThenInclude method.

The SQL statement now becomes more complex. Not just the Books table is queried, but with my current SQL Server provider two SELECT statements are done accessing the Books, Chapters, and Users table using LEFT JOIN and INNER JOIN. Now just one time information from the database is retrieved instead when accessing every single book:

 SELECT [b].[BookId], [b].[AuthorId], [b].[EditorId], [b].[Publisher], [b].[ReviewerId], [b].[Title], [b.Editor].[UserId], [b.Editor].[Name], [b.Reviewer].[UserId], [b.Reviewer].[Name], [b.Author].[UserId], [b.Author].[Name]
      FROM [Books] AS [b]
      LEFT JOIN [Users] AS [b.Editor] ON [b].[EditorId] = [b.Editor].[UserId]
      LEFT JOIN [Users] AS [b.Reviewer] ON [b].[ReviewerId] = [b.Reviewer].[UserId]
      LEFT JOIN [Users] AS [b.Author] ON [b].[AuthorId] = [b.Author].[UserId]
      WHERE [b].[Publisher] LIKE N'Wrox' + N'%' AND (LEFT([b].[Publisher], LEN(N'Wrox')) = N'Wrox')
      ORDER BY [b].[BookId]
SELECT [b.Chapters].[ChapterId], [b.Chapters].[BookId], [b.Chapters].[Number], [b.Chapters].[Title]
      FROM [Chapters] AS [b.Chapters]
      INNER JOIN (
          SELECT DISTINCT [b0].[BookId]
          FROM [Books] AS [b0]
          LEFT JOIN [Users] AS [b.Editor0] ON [b0].[EditorId] = [b.Editor0].[UserId]
          LEFT JOIN [Users] AS [b.Reviewer0] ON [b0].[ReviewerId] = [b.Reviewer0].[UserId]
          LEFT JOIN [Users] AS [b.Author0] ON [b0].[AuthorId] = [b.Author0].[UserId]
          WHERE [b0].[Publisher] LIKE N'Wrox' + N'%' AND (LEFT([b0].[Publisher], LEN(N'Wrox')) = N'Wrox')
      ) AS [t] ON [b.Chapters].[BookId] = [t].[BookId]
      ORDER BY [t].[BookId]

Instead of accessing the database with every property accessing a relation, the data is loaded early with less SQL statements sent to the database. Similar to explicit loading, the model doesn’t need special treatment. This scenario can also be used to return the model type and have all the associated data as needed when you can’t access the context instance to get additional data.


Using models to load related data is easy as long as the context is available. However, you need to pay attention not to create too many queries accessing the data from the database. If you know the relations needed in advance, you can use eager loading.
With lazy loading, proxy classes are created that derive from the model type. The proxy class overrides virtual properties to retrieve data needed dynamically.

The sample code is based on an EF Core sample from the book Professional C# 7 and .NET Core 2.0. In the book and in the book’s source code repository you can find samples with eager and explicit loading.

If you’ve read this far, consider buying me a coffee which helps me staying up longer and writing more articles.

Buy Me A Coffee

Interesting Links for this article:

Mapping to Getter-only Properties with EF Core

C# 8 & No More NullReferenceExceptions – What about legacy code?

Castle Project

Code Samples for Professional C# 7 and .NET Core 2.0

Complete Sample Code for this article

More information on EF Core and writing data-driven applications is in my book Professional C# 7 and .NET Core 2.0, and in my Data Programming workshops.

Enjoy learning and programming!


3 thoughts on “Lazy Loading with EF Core

  1. If you define your navigation property virtual, Entity Framework will at runtime create a new class (dynamic proxy) derived from your class and uses it instead of your original class. This new dynamically created class contains logic to load the navigation property when accessed for the first time. This is referred to as “lazy loading”. It enables Entity Framework to avoid loading an entire tree of dependent objects which are not needed from the database.…/virtual-in-entity-framework/



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.