Entity Framework Core Mastery

Lazy Loading Pitfalls & Proxies

1 Views Updated 5/4/2026

Lazy Loading Pitfalls & Proxies

Lazy Loading is the "magic" capability where EF Core automatically pauses your C# code and reaches out to the database to fetch relational data the exact millisecond you access a navigation property. While it seems incredibly convenient, it is widely considered the most dangerous anti-pattern in modern API development.

1. Enabling Lazy Loading (Opt-in)

Unlike older versions of Entity Framework, EF Core completely disables Lazy Loading by default to protect you. If you want it, you must explicitly install the Proxies package.

dotnet add package Microsoft.EntityFrameworkCore.Proxies

Configuration

// 1. Activate it in Program.cs
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer("...")
           .UseLazyLoadingProxies()); // Danger zone enabled

// 2. You MUST mark your Navigation Properties as "virtual"
public class Blog
{
    public int Id { get; set; }
    // The 'virtual' keyword allows EF Core to create a dynamic "Proxy" wrapper class
    // that overrides this property with database-fetching logic
    public virtual ICollection<Post> Posts { get; set; } 
}

2. The N+1 Query Problem (The Application Killer)

If you loop through data using Lazy Loading, you will accidentally execute hundreds of physical SQL queries without realizing it.

❌ The Silent Killer
// 1 Query fired: SELECT * FROM Blogs
var blogs = _context.Blogs.ToList();

// Suppose there are 500 blogs.
foreach(var blog in blogs)
{
    // EVERY TIME this loop iterates, the 'virtual' Posts property triggers an invisible SQL query!
    // Result: 500 physical database queries fired in extreme rapid succession.
    Console.WriteLine(blog.Posts.Count);
}

Total Queries: N + 1 (500 + 1). This will cripple your database server and cause your Web API endpoint to take 15 seconds to load.

3. Fixing It: Go back to Eager Loading

To fix the N+1 problem, you explicitly disable Lazy Loading and revert to .Include().

// Firing exactly ONE query with a SQL JOIN!
var blogs = _context.Blogs.Include(b => b.Posts).ToList();

foreach(var blog in blogs)
{
    // No database hits occur here, the data is already in RAM!
    Console.WriteLine(blog.Posts.Count);
}

4. Interview Mastery

Q: "We enabled Lazy Loading. In our Web API Controller, we `return Ok(blog);` to send the entity to the frontend. The API immediately crashes with an immense `System.Text.Json.JsonException: A possible object cycle was detected`. Why did enabling Lazy Loading destroy our JSON serialization?"

Architect Answer: "When `System.Text.Json` converts your `Blog` object into a JSON string, it walks through every property on the C# class. When it hits the `virtual Posts` property, the Lazy Loading Proxy instantly fires a DB query to fetch the Posts. Then, the JSON serializer walks into the `Post` object, hits the `virtual Blog` property, and fires ANOTHER DB query to get the parent Blog. It loops infinitely between the Parent and Child until the server throws a StackOverflow or an Object Cycle exception. This is why you should never return physical EF Core entities directly from an API; you must project them into clean `DTOs` to sever the connection to the Lazy Loading proxies, or fundamentally disable Lazy Loading entirely."

Entity Framework Core Mastery
1. Foundations & Architecture
Introduction to Object Relational Mapping (ORM) Entity Framework Core Architecture & Providers Setup and DbContext Integration Code-First vs Database-First Approaches Reverse Engineering Existing Databases (Scaffolding)
2. Code-First Modeling
Entity Conventions & Data Annotations The Fluent API Deep Dive (OnModelCreating) Primary Keys, Composite Keys, & Guids Required Properties & Database Defaults Value Conversions (Enums & Strongly Typed IDs)
3. Relational Architecture
One-to-Many Relationships & Foreign Keys One-to-One Relationships (Dependent Entities) Many-to-Many Relationships & Navigation Properties Owned Entity Types (Value Objects) Table-per-Hierarchy (TPH) Inheritance
4. Data Querying & LINQ
Basic LINQ Queries & IQueryable Execution Tracking vs No-Tracking Queries (Performance) Eager Loading vs Explicit Loading (Include) Lazy Loading Pitfalls & Proxies Client vs Server Evaluation Parsing
5. Manipulating Data (CUD)
Adding, Updating, and Removing Entities The ChangeTracker and Entity States Disconnected Entities in Web APIs Batch Updates and Deletes (.NET 7+)
6. Advanced Performance & Scale
Concurrency Tokens and Optimistic Locking Raw SQL Queries and Views (FromSqlRaw) Compiled Queries for High Throughput Interceptors (Logging & Auditing Data Changes) DbContext Pooling Mechanisms Managing Complex EF Core Migrations (CI/CD)