Skip to main content

Command Palette

Search for a command to run...

Entity Framework Core - Loading Strategies

Published
4 min read

This document outlines the four primary mechanisms for loading related data in Entity Framework Core, including syntax, use cases, and performance implications.


Quick Comparison

StrategyMechanismKey MethodBest For
Eager LoadingLoad everything upfront in one query..Include()Standard Read operations, APIs, Exports.
Lazy LoadingLoad on-demand when property is accessed.virtual propsRapid application development (RAD), Desktop apps.
Explicit LoadingLoad manually at a later point..Entry().Load()Conditional logic, complex business rules.
Select LoadingProject 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

  1. Enable proxies in OnConfiguring: .UseLazyLoadingProxies().

  2. 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?

  1. Do you need to modify/update the data?

    • No: Use Select Loading (Projections). It is significantly faster.

    • Yes: Go to step 2.

  2. Do you know exactly what related data you need?

    • Yes: Use Eager Loading (Include).

    • No (Conditional): Go to step 3.

  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.

More from this blog

E

EF Core

31 posts