Introduction
When working with Entity Framework Core (EF Core), LINQ queries make it easy to fetch and manipulate data. However, if not used carefully, they can lead to serious performance issues. One of the most common problems developers face is the N+1 query problem.
This issue can silently slow down your application by executing multiple unnecessary database queries. In real-world applications with large datasets, this can significantly impact performance and scalability.
In this article, we will understand what the N+1 problem is, why it happens, and how to optimize LINQ queries in EF Core using simple and practical techniques.
What is the N+1 Problem?
The N+1 problem occurs when your application executes:
1 query to fetch the main data
N additional queries to fetch related data (one for each record)
Example of N+1 Problem
var orders = context.Orders.ToList();
foreach (var order in orders)
{
Console.WriteLine(order.Customer.Name);
}
What Happens Internally?
First query → Fetch all orders
Then for each order → Fetch customer separately
If you have 100 orders, this results in 101 queries.
This is highly inefficient.
Why Does the N+1 Problem Happen?
The main reason is lazy loading or accessing navigation properties without explicitly including related data.
When EF Core sees order.Customer, it loads the customer data only when needed, triggering separate queries.
How to Detect the N+1 Problem
You can identify it by:
Watching SQL logs
Using profiling tools
Noticing slow performance
Seeing repeated similar queries in logs
Best Ways to Optimize LINQ Queries in EF Core
1. Use Eager Loading with Include()
The most common solution is using Include() to load related data in a single query.
var orders = context.Orders
.Include(o => o.Customer)
.ToList();
Why This Works
Fetches orders and customers together
Reduces multiple queries into one
2. Use ThenInclude() for Nested Data
When you have deeper relationships:
var orders = context.Orders
.Include(o => o.Customer)
.ThenInclude(c => c.Address)
.ToList();
This ensures all related data is loaded efficiently.
3. Use Select() for Projection (Best Practice)
Instead of loading full entities, select only required fields.
var orders = context.Orders
.Select(o => new
{
o.Id,
CustomerName = o.Customer.Name
})
.ToList();
Benefits
Reduces data transfer
Improves performance
Avoids unnecessary joins
4. Disable Lazy Loading
Lazy loading is a common cause of N+1 issues.
You can disable it:
options.UseLazyLoadingProxies(false);
This forces you to explicitly load related data.
5. Use Explicit Loading When Needed
Explicit loading gives you control over when data is fetched.
var order = context.Orders.First();
context.Entry(order)
.Reference(o => o.Customer)
.Load();
This is useful when you need data conditionally.
6. Use AsNoTracking() for Read-Only Queries
var orders = context.Orders
.AsNoTracking()
.Include(o => o.Customer)
.ToList();
Benefits
Improves performance
Reduces memory usage
7. Use Split Queries for Large Data
EF Core allows splitting queries to avoid complex joins:
Bohdan StupakPosted Apr 8, 2026, 1:05 PM
Thanks for the article. One small suggestion: consider using .ToListAsync(cancellationToken) instead of .ToList() so you don't create a blocking call.