Entity Framework Core - Loading Strategies
This document outlines the four primary mechanisms for loading related data in Entity Framework Core, including syntax, use cases, and performance implications.
Quick Comparison
| Strategy | Mechanism | Key Method | Best For |
| Eager Loading | Load everything upfront in one query. | .Include() | Standard Read operations, APIs, Exports. |
| Lazy Loading | Load on-demand when property is accessed. | virtual props | Rapid application development (RAD), Desktop apps. |
| Explicit Loading | Load manually at a later point. | .Entry().Load() | Conditional logic, complex business rules. |
| Select Loading | Project specific columns directly to DTOs. | .Select() | High Performance, Read-only lists, Reports. |
1. Eager Loading
Fetches the main entity and related data in a single database round-trip (usually via SQL JOIN).
Basic Syntax
Use Include for direct relationships and ThenInclude for nested (grandchild) relationships.
var blogs = context.Blogs
.Include(b => b.Posts) // Load Posts
.ThenInclude(p => p.Comments) // Load Comments for those Posts
.ToList();
Advanced: Filtered Include (EF Core 5+)
You can apply LINQ operations directly inside the Include to limit which related data is loaded.
var blogs = context.Blogs
.Include(b => b.Posts.Where(p => p.Rating > 3)) // Only load high-rated posts
.ToList();
Advanced: AutoInclude (EF Core 6+)
You can configure a relationship to always load automatically at the model level.
// In OnModelCreating
modelBuilder.Entity<Blog>().Navigation(b => b.Posts).AutoInclude();
- Note: To disable this for a specific query, use
.IgnoreAutoIncludes().
✅ Pros: Predictable performance; avoids N+1 problem.
❌ Cons: Can retrieve too much data (Over-fetching) if not careful.
2. Lazy Loading
Defers loading until the navigation property is actually accessed in code.
Requires Microsoft.EntityFrameworkCore.Proxies package.
Setup
Enable proxies in
OnConfiguring:.UseLazyLoadingProxies().Make navigation properties
virtual.
public class Blog {
public int Id { get; set; }
// "virtual" enables the proxy to intercept the call
public virtual ICollection<Post> Posts { get; set; }
}
// Usage
var blog = context.Blogs.First(); // Query 1: Load Blog
var count = blog.Posts.Count; // Query 2: Triggered automatically here
✅ Pros: Convenience; loads only what is strictly touched by the code.
❌ Cons: High Risk of N+1 Problem. Looping through 1,000 items and touching a property will trigger 1,001 database queries.
3. Explicit Loading
Loads the main entity first, then manually loads related data later via the Change Tracker API.
Basic Syntax
var blog = context.Blogs.First(b => b.Id == 1);
// Logic here... deciding if we need posts...
context.Entry(blog)
.Collection(b => b.Posts)
.Load(); // Explicitly fetches posts now
Advanced: Querying before Loading
Unlike Lazy loading, you can filter explicit loads to save memory.
context.Entry(blog)
.Collection(b => b.Posts)
.Query() // Returns IQueryable<Post>
.Where(p => p.IsArchived == false)
.Load();
✅ Pros: Full control over when and what is loaded; supports filtering.
❌ Cons: More verbose code; requires access to the DbContext instance.
4. Select Loading (Projections)
The Performance King. Instead of loading full Entities (which EF must track), you project exactly the columns you need into a DTO or anonymous type.
Syntax
var blogDtos = context.Blogs
.Select(b => new BlogDto
{
Name = b.Name,
// EF Core translates this sub-query automatically:
FirstPostTitle = b.Posts.OrderBy(p => p.Date).FirstOrDefault().Title
})
.ToList();
✅ Pros: Fastest option. Fetches only specified columns (no SELECT *). Bypasses Change Tracking overhead.
❌ Cons: Returns Read-Only data (cannot update/save changes back to DB easily).
Decision Matrix: Which one to use?
Do you need to modify/update the data?
No: Use Select Loading (Projections). It is significantly faster.
Yes: Go to step 2.
Do you know exactly what related data you need?
Yes: Use Eager Loading (
Include).No (Conditional): Go to step 3.
Is the code running in a loop or performance-critical path?
Yes: Avoid Lazy Loading. Use Explicit Loading carefully.
No (e.g., single item detail screen): Lazy Loading is acceptable for convenience.

