EF Core: Lazy Loading, Eager Loading, and Loading Data on Demand

posted 3 min read

When working with Entity Framework Core (EF Core), there are different strategies for loading related data from the database.

So, how do you load a collection or other related objects?

Here are the main options:

1️⃣ Eager Loading (Include)

Loads related data immediately with the main entity using .Include().

Example: Load an Order with its Customer

var order = context.Orders
    .Include(o => o.Customer) // Load related Customer
    .First();

Example: Load a Customer with Their Orders

var customer = context.Customers
    .Include(c => c.Orders) // Load related Orders
    .First();

✅Pros:

✔ Avoids multiple database queries (N+1 problem)

✔ Simple and easy to use

❌ Cons:

❌ Can load unnecessary data, increasing query size

❌ Slower if you load too many relationships

2️⃣ Explicit Loading (Entry().Reference() / Entry().Collection())

Loads related data on demand (manually) using .Entry().

Example: Load a Single Related Entity

var order = context.Orders.First();

// Load related Customer manually
context.Entry(order).Reference(o => o.Customer).Load();

Console.WriteLine(order.Customer.Name);

Example: Load a Collection of Related Entities

var customer = context.Customers.First();

// Load related Orders manually
context.Entry(customer).Collection(c => c.Orders).Load();

Console.WriteLine($"Orders count: {customer.Orders.Count}");

✅ Pros:

✔ More control over when data is loaded

✔ Avoids unnecessary queries

❌ Cons:

❌ Requires additional queries

❌ Must remember to load data explicitly

3️⃣ Lazy Loading (Requires Proxies)

Loads related data only when accessed.

Install :

dotnet add package Microsoft.EntityFrameworkCore.Proxies

Steps to Enable Lazy Loading

Enable in DbContext:

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
    options.UseLazyLoadingProxies().UseSqlite("Data Source=mydb.db");
}

Make navigation properties virtual

public virtual Customer Customer { get; set; } = default!;
public virtual List<Order> Orders { get; set; } = new();

Example Usage :

var order = context.Orders.First();

// Lazy loaded automatically when accessed

Console.WriteLine(order.Customer.Name);

✅ Pros:

✔ Simplifies code (data is loaded automatically)

✔ Works without modifying queries

❌ Cons:

❌ Requires proxies (extra package)

❌ Can lead to multiple database hits (N+1 problem)

4️⃣ Projection (Select with Anonymous Types)

Loads only specific data instead of full entities.

Example: Load Only Needed Fields

var orders = context.Orders
    .Select(o => new 
    { 
        o.OrderNumber, 
        CustomerName = o.Customer.Name 
    })
    .ToList();

foreach (var order in orders)
{
    Console.WriteLine($"{order.OrderNumber} - {order.CustomerName}");
}

✅ Pros:

✔ Best for performance (loads only required fields)

✔ Reduces unnecessary data transfer

❌ Cons:

❌ Cannot track entities (only works for read-only queries)

Summary Table

Best Practices
Use Eager Loading (Include()) when related data is always needed.

Use Explicit Loading if you want manual control.

Use Lazy Loading only if necessary (it can cause performance issues).

Use Projection (Select) when you need only some fields.

Source Code

You can find the complete source code for this tutorial at:
GitHub Repository

Here are some useful references to learn more about EF Core :

Official Microsoft Documentation
Entity Framework Core Loading Related Data
https://learn.microsoft.com/en-us/ef/core/querying/related-data

Covers Eager Loading, Explicit Loading, and Lazy Loading with examples.
EF Core: Configuring Lazy Loading
https://learn.microsoft.com/en-us/ef/core/querying/related-data/lazy

Explains how to enable Lazy Loading with proxies.
EF Core: Using Include() for Eager Loading
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager

Details how to use .Include() and .ThenInclude().
EF Core Performance Considerations
https://learn.microsoft.com/en-us/ef/core/performance/

Best practices for optimizing queries and avoiding performance issues like N+1 queries.

Community & Blog Articles
EF Core Lazy Loading vs. Eager Loading - Which One Should You Use?
https://www.thinktecture.com/en/entity-framework-core/lazy-loading-vs-eager-loading/

A detailed comparison of when to use each strategy.
EF Core Best Practices: Avoiding Performance Pitfalls
https://www.michalbialecki.com/2020/11/29/entity-framework-core-performance-best-practices/

Covers when to use .Include(), lazy loading, and projection.

If you read this far, tweet to the author to show them you care. Tweet a Thanks
great breakdown of EF Core loading strategies!  Appreciate the effort in comparing them. Quick question—when working with large datasets, is Projection (Select) always the best choice, or can Eager Loading still be useful in some cases? I love the references you always provide.... :-)
Thanks! I really appreciate that.
Using projection (Select) is usually the best option for large datasets because it only loads the data you need. This helps save memory and improves performance.
However, eager loading can still be useful when you know you will need related data. It helps reduce multiple queries, especially when working with batch processing or avoiding the N+1 problem.

The best choice depends on the situation. If you only need a few fields for a list, projection is better. But if you are working with a smaller dataset or need to load related data, eager loading can also be a good option.

What do you think? Have you used one of these methods in a real project?
In my experience, i usually default to projection for efficiency, but if I knowI will  need related data across multiple entities, eager loading can be a smarter choice
It might be interesting to create a demo application and run some benchmarks to compare different approaches. It could provide valuable insights—of course, it depends on how much time we have available. What do you think?

More Posts

EF Core Global Query Filters: A Complete Guide

Spyros - Mar 2

When NOT to Use AsSplitQuery() in EF.core

Spyros - Feb 26

Rate limiting middleware in ASP.NET Core using .NET 8.0

Hussein Mahdi - May 11, 2024

What's New in .NET Aspire 9.1

Barret Blake - Mar 13

Suitable DB Choice in USSD Application

Moses Korir - Mar 12
chevron_left