Lazy loading of data is a pattern whereby the retrieval of data from the database is deferred until it is needed. This sounds like a good thing, and in some scenarios, this can help to improve the performance of an application. In other scenarios, it can degrade the performance of an application substantially, particularly so in web applications. For this reason, lazy loading was introduced in EF Core 2.1 as an opt-in feature.
Enabling Lazy Loading
Lazy loading can be enabled in two ways:
- Using Proxies
- Using the
ILazyLoaderservice
Proxies
Proxies are objects deriving from your entities that are generated at runtime by Entity Framework Core. These proxies have behavior added to them that results in a database query being made as required to load navigation properties on demand. This was the default mechanism used to provide lazy loading in the previous version of Entity Framework.
Enabling lazy loading by proxies requires three steps:
- Install the Microsoft.EntityFrameworkCore.Proxies package
language-cmd|
[Package Manager Console] install-package Microsoft.EntityFrameworkCore.Proxies [Dotnet CLI] add package Microsoft.EntityFrameworkCore.Proxies - Use the
UseLazyLoadingProxiesmethod to enable the creation of proxies in theOnConfiguringmethod of theDbContext:language-csharp|protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLazyLoadingProxies(); } - Make all navigation properties
virtual:language-csharp|public class Author { public int AuthorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual List<Book> Books { get; set; } = new List<Book>(); } public class Book { public int BookId { get; set; } public string Title { get; set; } public int AuthorId { get; set; } public virtual Author Author { get; set; } }
This last step is the key to allowing EF Core to override your entities to create proxies. In addition, all entity types must be public, unsealed, and have a public or protected constructor.
ILazyLoader
The ILazyLoader interface represents a component that is responsible for loading navigation properties if they haven't already been loaded. This approach circumvents the generation of proxies which isn't supported on all platforms. ILazyLoader can be used in one of two ways. It can be injected into the principal entity in the relationship, where it is used to load dependents. This requires that your model class(es) take a dependency on Microsoft.EntityFrameworkCore.Infrastructure, which is available in the Microsoft.EntityFrameworkCore.Abstractions package. Or you can use a convention-based delegate.
The following steps detail how to employ the first approach:
Install the Microsoft.EntityFrameworkCore.Abstractions package into the project containing your model classes:
language-cmd|[Package Manager Console] install-package Microsoft.EntityFrameworkCore.Abstractions [Dotnet CLI] add package Microsoft.EntityFrameworkCore.AbstractionsAlter the principal entity to include
a
usingdirective forMicrosoft.EntityFrameworkCore.Infrastructurea field for the
ILazyLoaderinstancean empty constructor, and one that takes an
ILazyLoaderas a parameter (which can be private, if you prefer)a field for the collection navigation property
a getter in the public property that uses the
ILazyLoader.Loadmethodlanguage-csharp|using Microsoft.EntityFrameworkCore.Infrastructure; public class Author { private readonly ILazyLoader _lazyLoader; public Author() { } public Author(ILazyLoader lazyLoader) { _lazyLoader = lazyLoader; } private List<Book> _books; public int AuthorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<Book> Books { get => _lazyLoader.Load(this, ref _books); set => _books = value; } }
Whether you have used proxies or the ILazyLoader interface, lazy loading is now enabled in your application, and will take place as soon as you reference dependent entities in a relationship:
using(var db = new BookContext())
{
var authors = db.Authors;
foreach(var author in authors)
{
Console.WriteLine($"Name: {author.FirstName} {author.LastName}");
foreach(var book in author.Books) // lazy loading initiated
{
Console.WriteLine($"\t{book.Title}");
}
}
}
If you add logging, you can see the SQL commands that are executed against the database (each one highlighted in yellow):

The first query retrieves the authors, and then there are n more queries, where n represents the number of results from the first query. This is known as the n+1 query pattern, or probably more accurately, the n+1 problem. Flooding the database with unnecessary queries can cause performance problems. The same result set can be obtained with two queries using Include:
var authors = db.Authors.Include(a => a.Books);
foreach(var author in authors)
{
Console.WriteLine($"Name: {author.FirstName} {author.LastName}");
foreach(var book in author.Books)
{
Console.WriteLine($"\t{book.Title}");
}
}

The advice is not to use lazy loading unless you are certain that it is the better solution. This is why (unlike in previous versions of EF) lazy loading is not enabled by default in Entity Framework Core.