8 minlesson

Loading Strategies Overview

Loading Strategies Overview

When you query an entity in EF Core, its navigation properties are not loaded by default. This is a deliberate design decision that gives you control over how much data is retrieved from the database.

Why Related Data Isn't Loaded Automatically

Consider a Blog entity with thousands of Posts, each with hundreds of Comments. If EF Core automatically loaded all related data every time you queried a blog, a simple lookup could pull the entire database into memory.

csharp
1// This returns a Blog with Posts = null (not loaded)
2var blog = await context.Blogs.FirstAsync(b => b.Id == 1);
3
4Console.WriteLine(blog.Title); // Works fine
5Console.WriteLine(blog.Posts.Count); // NullReferenceException!

EF Core requires you to explicitly declare when and how related data should be loaded. This keeps queries efficient and predictable.

The Three Loading Strategies

EF Core provides three strategies for loading related data:

StrategyWhen Data Is LoadedMethod
Eager LoadingAt query time, in the same queryInclude() / ThenInclude()
Explicit LoadingOn demand, after the parent is loadedEntry().Collection().LoadAsync()
Lazy LoadingAutomatically when navigation is accessedVirtual properties + proxies

Eager Loading

Eager loading retrieves related data as part of the initial query using Include(). The related data is loaded in one round trip to the database.

csharp
1var blog = await context.Blogs
2 .Include(b => b.Posts)
3 .FirstAsync(b => b.Id == 1);
4
5// blog.Posts is populated - no additional queries needed
6foreach (var post in blog.Posts)
7{
8 Console.WriteLine(post.Title);
9}

Best for: When you know upfront that you need the related data. Most common strategy.

Explicit Loading

Explicit loading lets you load related data after the parent entity is already in memory. You control exactly when the additional query runs.

csharp
1var blog = await context.Blogs.FirstAsync(b => b.Id == 1);
2
3// Load posts on demand
4await context.Entry(blog)
5 .Collection(b => b.Posts)
6 .LoadAsync();

Best for: When you conditionally need related data based on some logic.

Lazy Loading

Lazy loading automatically loads related data when you first access a navigation property. It requires configuration with proxies or injection.

csharp
1// With lazy loading configured:
2var blog = await context.Blogs.FirstAsync(b => b.Id == 1);
3
4// Accessing Posts triggers a database query automatically
5foreach (var post in blog.Posts) // Query happens here
6{
7 Console.WriteLine(post.Title);
8}

Best for: Prototyping or when access patterns are unpredictable. Use with caution in production.

Choosing the Right Strategy

1Do you always need the related data?
2├── Yes → Eager Loading (Include)
3└── No
4 ├── Do you need it conditionally? → Explicit Loading
5 └── Is it unpredictable? → Lazy Loading (with caution)

General guidance:

  • Start with eager loading - it is the most explicit and predictable approach
  • Use explicit loading when the decision to load depends on runtime conditions
  • Avoid lazy loading in production web applications - it causes the N+1 query problem, where accessing a list of parents with their children generates one query per parent instead of a single batch query

Key Takeaways

  1. EF Core does not load navigation properties by default
  2. Eager loading (Include) loads related data in the initial query
  3. Explicit loading (Entry().LoadAsync()) loads data on demand after the parent is loaded
  4. Lazy loading loads data automatically on property access but risks N+1 queries
  5. Eager loading is the recommended default for most scenarios
Loading Strategies Overview - Anko Academy