Skip to main content

N+1

Find N+1 queries in .NET and EF Core code, at build time and in tests

dotnet add package NPlusOne.Analyzers
OrderService.cs(42,31): warning NP0001: 'IOrderRepository.GetByCustomerAsync' runs a query
('ToListAsync' in OrderRepository.GetByCustomerAsync) once per element of the foreach loop over 'customers'.
Load the rows for all elements up front with one query, Where(o => keys.Contains(o.CustomerId)),
and group them with ToLookup(o => o.CustomerId).

At build time

A Roslyn analyzer follows queries, lazy loads and saves through loops, LINQ lambdas, services, interfaces, mediators, mappers and serializers, and reports the ones that run once per element. Precision first: it warns only when it can prove the round trip.

In tests and requests

A runtime guard watches the SQL EF Core really sends inside a scope and fails a test, or flags a request, when the same query or lazy load repeats. It catches what no compiler can see.

Proven on a real database

Every one of the 143 known N+1 shapes in the catalogue is a test case run on SQLite: a reported case must send more SQL for more rows, a silent one the same amount, on EF Core 8, 9 and 10.

Three packages

Released together under one version, all on NuGet. Use the analyzer, the guard, or both.

Build time

NPlusOne.Analyzers

The Roslyn analyzer, its code fixes and the generator that follows calls across projects. A development dependency: nothing ships with your app.

dotnet add package NPlusOne.Analyzers
Tests

NPlusOne.EntityFrameworkCore

The runtime guard: UseNPlusOneGuard() on the DbContext, a scope per test, AssertNoNPlusOne(). Any test framework, no DI needed.

dotnet add package NPlusOne.EntityFrameworkCore
Requests

NPlusOne.AspNetCore

One guard scope per HTTP request, [NPlusOneAssert] for MVC and .WithNPlusOneAssert() for minimal APIs. Brings the guard with it.

dotnet add package NPlusOne.AspNetCore