NPlusOne.EntityFrameworkCore 1.0.0

dotnet add package NPlusOne.EntityFrameworkCore --version 1.0.0
                    
NuGet\Install-Package NPlusOne.EntityFrameworkCore -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NPlusOne.EntityFrameworkCore" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NPlusOne.EntityFrameworkCore" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="NPlusOne.EntityFrameworkCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NPlusOne.EntityFrameworkCore --version 1.0.0
                    
#r "nuget: NPlusOne.EntityFrameworkCore, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NPlusOne.EntityFrameworkCore@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NPlusOne.EntityFrameworkCore&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=NPlusOne.EntityFrameworkCore&version=1.0.0
                    
Install as a Cake Tool

N+1

CI NuGet License: MIT

Finds N+1 query problems in .NET and EF Core code: at build time with a Roslyn analyzer, and in tests and requests with a runtime guard. Free and open source (MIT).

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).

Documentation: selcukgural.github.io/NPlusOne

Quick start

1. Add the analyzer to every project with EF Core code (EF Core 8, 9 or 10; any SDK since .NET 8):

dotnet add package NPlusOne.Analyzers

2. Build. N+1 queries show up as warnings, in the build and in the IDE, each with the rewrite that removes it:

dotnet build

3. Keep them out. Raise the rules to errors in .editorconfig:

[*.cs]
dotnet_diagnostic.NP0001.severity = error
dotnet_diagnostic.NP0002.severity = error
dotnet_diagnostic.NP0003.severity = error
dotnet_diagnostic.NP0005.severity = error

4. Catch the rest in tests. The runtime guard fails a test when the same query or lazy load repeats:

dotnet add package NPlusOne.EntityFrameworkCore
var options = new DbContextOptionsBuilder<ShopContext>().UseSqlite(connection).UseNPlusOneGuard().Options;

using var guard = NPlusOneGuard.Begin();
await new OrderService(new ShopContext(options)).ListAsync();
guard.AssertNoNPlusOne();

5. Watch requests in development with NPlusOne.AspNetCore: builder.Services.AddNPlusOneGuard() and app.UseNPlusOneGuard().

The getting started guide walks through each step.

What it finds

N+1 hides in more places than a foreach with a query in it. The analyzer follows round trips through:

Loops for, foreach, await foreach, while, do, indexes and deconstructed elements
Callbacks LINQ lambdas, method groups, List.ForEach, Parallel.ForEach, Task.WhenAll(ids.Select(...)), EF Core's ForEachAsync, FluentValidation RuleForEach, client-evaluated projections, your own iterating helpers
Calls helpers, services, interfaces and virtual methods (to their implementations, across projects), extension and generic methods, delegates, compiled queries, getters and indexers, recursion
Libraries MediatR and Mediator (handlers, pipeline behaviors, streams), AutoMapper, Mapster, Mapperly
Lazy loading navigations read per element, checked against the query's Includes and AutoIncludes, also in ToString, Equals, conversions and patterns
Endpoints MVC, minimal APIs, SignalR and serializers given lazily loaded entities, also inside pages and anonymous objects; OData and [JsonIgnore] respected
Writes SaveChanges and transactions per element

and it stays quiet where a loop is meant to make one round trip per iteration: batches, paging, polling, search loops, cached lookups, and anything EF Core translates into the same SQL statement.

Every one of the 132 shapes in the catalogue is a test that runs on SQLite and counts the SQL: a reported case must send more commands for more rows, a silent one the same number, on EF Core 8, 9 and 10.

Rules

Rule Default
NP0001 A query runs once per element (N+1 read) Warning
NP0002 A lazy-loaded navigation is read once per element (code fix: add the Include) Warning
NP0003 SaveChanges runs once per element (code fix: move it after the loop) Warning
NP0004 A recursive method runs a query on every call Warning
NP0005 An endpoint returns entities whose lazy navigations the serializer loads one by one Warning
NP0006 A data-access abstraction is called once per element and its implementation is not visible Info
NP0007 A mapper reads lazy navigations of each element by naming convention Info

Options (lazy loading, methods the analyzer cannot see into, call depth) are listed in configuration.

Packages

Package Use it for
NPlusOne.Analyzers The analyzer, its code fixes and the summary generator that follows calls across projects. A development dependency.
NPlusOne.EntityFrameworkCore The runtime guard: scopes in tests or anywhere, AssertNoNPlusOne, reports with normalized SQL and call sites, never parameter values.
NPlusOne.AspNetCore One guard scope per request, [NPlusOneAssert] for MVC and .WithNPlusOneAssert() for minimal APIs.

How it compares

Measured on the same proven cases, the analyzer finds 231 of 231 N+1 methods with no false warning; LinqContraband, the static analyzer with the most N+1 rules we found, finds 53. At run time the guard here, EFCore.QueryGuard and DbEye all see most cases; the guard alone also sees SQL that differs per element, transactions per element, and counts one batched SaveChanges once. The corpus was written for this tool, so read the comparison for what each number means.

Contributing

dotnet build and dotnet test (SDK pinned in global.json; the .NET 8, 9 and 10 runtimes run the tests). A missed N+1 or a false warning starts as a corpus case; see CONTRIBUTING.md and how it works.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on NPlusOne.EntityFrameworkCore:

Package Downloads
NPlusOne.AspNetCore

ASP.NET Core integration for the N+1 runtime guard: one scope per request, with a middleware and an MVC filter.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 25 10/13/2026