If you have been working with Entity Framework Core for a while, you have probably written a query that looked completely innocent, only to discover later that your application was making hundreds of database queries.
The annoying part is that the C# code can look perfectly reasonable.
This is one of those problems that is easy to miss during development because everything works fine when you have 10 records in your database. Then production arrives with 10,000 records, and suddenly the database is doing a lot more work than you expected.
What is the N+1 problem?
Imagine we have a simple application.
We have:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; } = null!;
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}Now we want to display all orders together with the customer's name.
A beginner might write:
var orders = await context.Orders.ToListAsync();
foreach (var order in orders)
{
Console.WriteLine(order.Customer.Name);
}At first glance, nothing looks wrong.
We execute one query to retrieve the orders.
Then we access order.customer.
But here's the problem:
// var orders = await context.Orders.ToListAsync();
|__ 1 query --> get all orders
// foreach (var order in orders)
// order.Customer.Name
|__ + 1 query --> get customer for order 1
|__ + 1 query --> get customer for order 2
|__ + 1 query --> get customer for order 3
|__ + ...
|__ + 1 query --> get customer for order NSo instead of executing one query, we execute:
1 + N queriesThat's where the name comes from.
If we have 100 orders: 1 + 100 = 101 queries
If we have 10,000 orders: 1 + 10,000 = 10,001 queries
The application hasn't changed. The amount of data has.
And suddenly a perfectly innocent-looking loop becomes a performance problem.
Why is it called N+1?
The 1 is the first query:
SELECT *
FROM Orders;The N represents the additional queries needed for the N records:
SELECT *
FROM Customers
WHERE Id = 1;
SELECT *
FROM Customers
WHERE Id = 2;
SELECT *
FROM Customers
WHERE Id = 3;The problem is the number of round trips to the database.
But does Entity Framework Core always cause N+1?
No.
This is an important distinction.
Simply having a navigation property does not automatically mean Entity Framework Core will execute another query whenever you access it.
For example, EF Core does not enable lazy loading by default.
N+1 commonly appears when you explicitly use lazy loading, manually query related entities inside a loop, or structure your application in a way that causes repeated database access.
For example, with lazy loading enabled:

Join the conversation! Your thoughts help the community grow.