How do you detect the N+1 query problem?
N+1 = 1 query for the parent + 1 per child row when accessing a navigation property.
Detection methods, in order of investment:
1. EF Core SQL logging (dev):
options.LogTo(Console.WriteLine, LogLevel.Information);
Look for the same SELECT shape repeated dozens of times in rapid succession. Each one differs only by the WHERE clause value.
2. Application Performance Monitoring (production): Datadog, Honeycomb, Application Insights, OpenTelemetry — they show a "sawtooth" of identical small DB spans on a single request. One trace tells you immediately.
3. Test-level query counting:
// Wrap commands with a counter; assert max queries per request.
var queryCount = 0;
db.SavingChanges += (_, _) => queryCount++;
var orders = await service.ListOrdersAsync();
Assert.That(queryCount, Is.LessThan(3));
Catches regressions in CI.
4. EXPLAIN ANALYZE the slow query:
Sometimes "N+1" is actually a single slow query missing an index. Run EXPLAIN ANALYZE to see the query plan.
Why it bites in production but not dev:
- Localhost network latency is microseconds → 100 queries feel instant
- Production network is milliseconds × 100 = whole seconds added
- Dev data is small → small Cartesian products
The fix — always one of:
.Include()for navigation properties you'll access.AsSplitQuery()for multi-collection includes.Select(o => new Dto { ... })projection — usually best
Rule: if any production request spawns more than 5 SQL queries, treat it as a defect until proven otherwise.