Skip to content

Entity Framework Lazy Loading Not Working Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Entity Framework Lazy Loading Not Working Fix. We cover key concepts, practical examples, and best practices.

Navigation properties return null even though the related data exists in the database:

var order = db.Orders.Find(1);
Console.WriteLine(order.Customer?.Name); // null

Lazy loading only works when your DbContext is not disposed and navigation properties are marked as virtual. EF Core requires explicit opt-in since version 3.0 — it is not enabled by default like in EF6.

Step-by-Step Fix

1. Install proxy package and enable lazy loading

WRONG — assuming lazy loading is automatic:

public class Order
{
    public int Id { get; set; }
    public Customer Customer { get; set; } // not virtual
}

RIGHT — make navigation properties virtual and enable lazy loading:

public class Order
{
    public int Id { get; set; }
    public virtual Customer Customer { get; set; }
}

Install the package:

dotnet add package Microsoft.EntityFrameworkCore.Proxies

Configure in OnConfiguring:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseLazyLoadingProxies()
                  .UseSqlServer(connectionString);
}

Or via AddDbContext:

services.AddDbContext<AppDbContext>(options =>
    options.UseLazyLoadingProxies()
           .UseSqlServer(connectionString));

2. Ensure DbContext is not disposed

WRONG — accessing navigation property after context disposal:

Customer customer;
using (var db = new AppDbContext())
{
    var order = db.Orders.Find(1);
    customer = order.Customer; // lazy load happens here
}
Console.WriteLine(customer.Name); // ObjectDisposedException

RIGHT — eager load with Include when working outside the context:

Customer customer;
using (var db = new AppDbContext())
{
    var order = db.Orders.Include(o => o.Customer).FirstOrDefault(o => o.Id == 1);
    customer = order.Customer;
}
Console.WriteLine(customer.Name);

3. Handle N+1 with lazy loading

WRONG — lazy loading causes N+1 queries in loops:

foreach (var order in db.Orders.ToList())
{
    Console.WriteLine($"{order.Id}: {order.Customer.Name}");
    // Each iteration fires a separate query for Customer!
}

RIGHT — eager load when iterating:

var orders = db.Orders.Include(o => o.Customer).ToList();
foreach (var order in orders)
{
    Console.WriteLine($"{order.Id}: {order.Customer.Name}");
}

Expected output: a single query with JOIN, not N+1 queries.

4. Fix serialization circular reference

WRONG — lazy loading triggers infinite serialization:

var orders = db.Orders.ToList();
return JsonSerializer.Serialize(orders); // circular reference error

RIGHT — disable lazy loading for serialization or use DTOs:

var orderDtos = db.Orders.Select(o => new OrderDto
{
    Id = o.Id,
    CustomerName = o.Customer.Name
}).ToList();
return JsonSerializer.Serialize(orderDtos);

Prevention

  • Use eager loading (Include/ThenInclude) by default; opt into lazy loading selectively.
  • Disable lazy loading in production APIs to prevent N+1 issues.
  • Use DTOs for serialization instead of entity classes.
  • Mark navigation properties as virtual only if lazy loading is enabled.
  • Never access navigation properties outside the DbContext's scope.

Common Mistakes with framework lazy loading

  1. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  2. Using return to exit a function early instead of wrapping a pure value in the monad
  3. Mixing let bindings with <- bindings in do notation, producing type errors

These mistakes appear frequently in real-world ENTITY code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Does lazy loading work with JSON serialization?

Not safely. JSON serializers typically access every property on an object, causing lazy loading to trigger hundreds of database queries. Always use DTOs or disable lazy loading before serializing entities.

How do I detect N+1 queries caused by lazy loading?

Enable EF Core logging: optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information). You'll see a separate SQL query for each lazy-loaded navigation. Use Include() to batch them into JOINs.

Can I use lazy loading with no-tracking queries?

Yes, but lazy loading still works with tracking queries. No-tracking queries return raw entity types (not proxies), so lazy loading will not work. Use Include() with no-tracking queries.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro