Skip to main content

N+1 catalogue

Every way EF Core code ends up running one round trip (query, lazy load, save) per element, what the analyzer must do about it, and where it stands. Each row is a case to check, test (a corpus case on both sides) and fix. Built on 2026-10-12 against the analyzer at 1a3749d plus item 6's first batch of corpus cases; probes compiled with the real MediatR 12.5, AutoMapper 14, Mapster 7.4 and Mediator 3.0 packages, EF Core 10, lazy loading on.

Every corpus case is also run against SQLite by GroundTruthTests (see docs/design.md); a row marked covered is proven at run time unless its corpus method says otherwise in a // ground-truth: comment.

Status:

  • covered: a corpus case guards it (file named).
  • works: a probe showed the right result, but no corpus case guards it yet; add one.
  • gap: missed / gap: false positive: the probe showed the wrong result; fix direction given.
  • runtime guard: static analysis cannot know it; the runtime guard catches it in tests.
  • not N+1: a constant number of round trips, or not a database round trip; listed so nobody re-checks it.
  • unverified: needs a package or provider the harness does not reference yet.

A. EF Core APIs​

#CaseSnippetExpectedStatusFix direction
A1Executing operator in a loopforeach (c in cs) await db.Orders.Where(...).ToListAsync()NP0001covered: Reads/Loops.cs
A2Less common sync operatorsdb.Orders.All(...), Contains, LongCount, Max, Average, LastOrDefault, ElementAtOrDefault, ToArray in a loopNP0001covered: Reads/WorksCases.cs
A3Less common async operatorsAllAsync, ContainsAsync, LongCountAsync, MaxAsync, ToDictionaryAsync, ToHashSetAsync, SingleOrDefaultAsync, ToArrayAsyncNP0001covered: Reads/WorksCases.cs
A4Find by key in a loopawait db.Orders.FindAsync(id)NP0001covered: Reads/Loops.cs
A5Set-based statements per elementdb.Orders.Where(o => o.Id == id).ExecuteDeleteAsync() / ExecuteUpdateAsyncNP0001covered: Reads/WorksCases.cs
A6Raw SQL per elementDatabase.ExecuteSqlAsync($"..."), FromSqlInterpolatedNP0001covered: Reads/Loops.cs
A7Other raw SQL APIsDatabase.SqlQuery<int>(...), ExecuteSqlRawAsync, FromSqlRawNP0001covered: Reads/WorksCases.cs
A8Explicit loading per elementdb.Entry(o).Reference(x => x.Customer).LoadAsync(), .Collection(...).Query().LoadAsync()NP0001covered: Reads/Loops.cs
A9Other entry round tripsEntry(o).Collection(...).Query().CountAsync(), ReloadAsync(), GetDatabaseValuesAsync()NP0001covered: Reads/WorksCases.cs
A10IsLoaded, State = per elementdb.Entry(o).Collection(x => x.Lines).IsLoadedsilentcovered: Reads/WorksCases.cs
A11Lazy loading with proxiesforeach (o in orders) o.Customer.NameNP0002covered: Lazy/LazyNavigation.cs
A12Lazy loading with ILazyLoader / Action<object, string>; plain auto-property navigations of such entitiesget => LazyLoader.Load(this, ref _posts)NP0002 / silentcovered: LazyLoader/LazyLoaderEntities.cs
A13Include, ThenInclude, string IncludeInclude(o => o.Customer), Include("Customer")silentcovered: Lazy/LazyNavigation.cs
A14Filtered IncludeInclude(o => o.Lines.Where(l => l.Price > 0))silentcovered: Lazy/WorksLazy.cs
A15Second level not includedInclude(c => c.Orders) then o.Lines per orderNP0002covered: Lazy/LazyNavigation.cs
A16AutoInclude() / IgnoreAutoIncludes()b.Navigation(x => x.Customer).AutoInclude()silent / NP0002covered: Lazy/LoadedWithoutInclude.cs
A17Untracked results, lazy loading turned offAsNoTracking(), AsNoTrackingWithIdentityResolution(), LazyLoadingEnabled = falseEF Core 8+: NP0002 (a shadow-FK reference throws: silent); EF Core ≤7 and LazyLoadingEnabled = false: silentcovered: Lazy/LoadedWithoutInclude.cs, Lazy/ComputedAndPassed.cs (shadow FK), run-time proven
A17bCollection back-reference under an included reference, serializeddb.Orders.Include(o => o.Customer) returned; Customer.Orders is not loadedNP0005 (a reference back-reference: silent)covered: Endpoints/HubsAndViews.cs, run-time proven
A18Split queriesInclude(...).ThenInclude(...).AsSplitQuery()silentcovered: Lazy/WorksLazy.cs
A19Compiled queries invoked per elementstatic readonly Func<AppDb, int, Task<Order>> ById = EF.CompileAsyncQuery(...); await ById(db, id) in a loopNP0001covered: Reads/Accessors.cs
A20DbSet.Local lookupsdb.Orders.Local.FirstOrDefault(...)silentcovered: Reads/WorksCases.cs
A21Add/Update/Remove/Attach without savingdb.Orders.Add(o) per elementsilentcovered: Writes/WorksWrites.cs
A22SaveChanges per elementawait db.SaveChangesAsync() in a loop, lambda or helperNP0003covered: Writes/SaveChangesPerElement.cs
A23Transaction per elementawait using var tx = await db.Database.BeginTransactionAsync(); ... await tx.CommitAsync(); in a loopNP0003covered: Writes/Transactions.cs
A24Correlated subquery or collection in a projectiondb.Customers.Select(c => new { c.Name, Orders = c.Orders.ToList() })silentcovered: Reads/NotNPlusOne.cs
A25Client-evaluated final projection calling a method that reads navigationsdb.Orders.Select(o => Describe(o)).ToListAsync(), Describe reads o.CustomerNP0002covered: Lazy/ClientProjection.cs
A26Client-evaluated final projection calling a service that queriesdb.Customers.Select(c => new { c.Id, N = counter.Count(c.Id) }) (also on the same context: SQLite runs it)NP0001covered: Reads/ClientProjection.cs
A27A new context per elementusing var ctx = new AppDb(...) / await factory.CreateDbContextAsync() in a loopNP0001covered: Reads/Loops.cs (factory: works)
A28Query built but not executed in a loopvar q = db.Orders.Where(...)silentcovered: Reads/NotNPlusOne.cs
A29Query captured outside, executed per elementvar q = db.Orders.Where(...); q.Count(...) in the loopNP0001covered: Reads/Loops.cs
A30Query typed as IEnumerable<T>, enumerated per elementIEnumerable<Order> big = db.Orders.Where(...); big.Count(...) in the loopNP0001covered: Reads/WorksCases.cs
A31AsEnumerable() in a loopdb.Orders.AsEnumerable().Where(...).ToList()NP0001covered: Reads/WorksCases.cs
A32Load() on a query per elementawait db.Orders.Where(...).LoadAsync()NP0001covered: Reads/CallChains.cs
A33await foreach over a query inside a loopawait foreach (var o in db.Orders.Where(...).AsAsyncEnumerable())NP0001covered: Reads/Loops.cs
A34foreach directly over a query, reading lazy navigationsforeach (var o in db.Orders) o.Customer.NameNP0002covered: Lazy/LazyNavigation.cs
A35Owned types (OwnsOne, OwnsMany)s.Origin.City per elementsilentcovered: Lazy/WorksLazy.cs
A36Many-to-many skip navigationss.Tags.Count per elementNP0002covered: Lazy/WorksLazy.cs
A37Navigations of derived types (TPH/TPT) when only the base type has a DbSetdb.Parties.OfType<Company>(); c.Sector.Label per elementNP0002covered: Lazy/DerivedTypes.cs
A38Query filters, tags, temporal operatorsIgnoreQueryFilters().TagWith(...), TemporalAsOf(...) in a loopNP0001covered: Reads/WorksCases.cs (filters and tags; temporal operators need the SQL Server provider and are not run)
A39Paging, polling, Chunk, search-until-unique loopswhile (...) { ...Skip(n).Take(100)... }silentcovered: Reads/NotNPlusOne.cs
A40Load-once guardsx ??= await querysilentcovered: Reads/NotNPlusOne.cs
A41Lazy collection counted per parentc.Orders.Count / c.Orders.Any()NP0002covered: Lazy/LazyNavigation.cs
A42Client-side GroupBy (EF Core 2 client evaluation)db.Orders.GroupBy(...).Select(g => g.ToList())silentnot N+1 (EF Core 3+ throws instead)
A43Cartesian explosion of several collection IncludesInclude(c => c.Orders).Include(c => c.Addresses)silentnot N+1 (one large query; AsSplitQuery is the fix)
A44Retries, timer polls, draining in pages, concurrency conflict handlingwhile (!saved) { try { db.SaveChanges(); saved = true; } catch (DbUpdateConcurrencyException e) { foreach (var entry in e.Entries) ... } }; a try around each element's save stays NP0003silent / NP0003covered: Writes/RetriesAndDrains.cs
A45Entities mapped only in the model configuration, without a DbSetmodelBuilder.Entity<Ledger>(), also in an extension method on ModelBuilder (ABP modules) or Entity(typeof(Receipt)); db.Set<Ledger>() then l.Owner.Name per elementNP0002 (silent for an Entity<T>() that is not EF Core's)covered: Lazy/ModelBuilderEntities.cs (was missed, fixed 2026-10-13)
A46A navigation read after loading it explicitlyawait db.Entry(o).Reference(x => x.Customer).LoadAsync(); o.Customer.Name (ABP: EnsurePropertyLoadedAsync); after a filtered Collection(...).Query().Where(...).LoadAsync() the collection is not loadedNP0001 on the load, the read silent; NP0002 after a filtered loadcovered: Lazy/WorksLazy.cs, Abp/BookServices.cs (was a false positive, fixed 2026-10-13)

B. C# and .NET constructs​

#CaseSnippetExpectedStatusFix direction
B1for, foreach, while, do, await foreachquery in the bodyNP0001covered: Reads/Loops.cs
B2LINQ to Objects lambdasSelect, Where, ToDictionary, SelectMany, List.ForEach, Array.ForEachNP0001covered: Reads/Lambdas.cs
B3More LINQ lambdasSum, OrderBy, Any, Aggregate, ToLookup element selector, Max, string.Join(",", cs.Select(...))NP0001covered: Reads/WorksCases.cs
B4Parallel and concurrentParallel.For/ForEach/ForEachAsync, Task.WhenAll(ids.Select(...)), Task.Run in a loopNP0001covered: Reads/Lambdas.cs
B5Query syntaxfrom c in cs let n = db.Orders.Count(...) select ..., select c.Address.CityNP0001 / NP0002covered: Reads/WorksCases.cs, Lazy/WorksLazy.cs
B6Iterators, local functionsyield return await db..., local function called in a loopNP0001covered: Reads/Lambdas.cs
B7Delegate in a localFunc<int, Task<int>> count = id => db...; await count(id) in a loopNP0001covered: Reads/StoredAndShared.cs
B8Delegate in a fieldprivate readonly Func<int, int> _count = id => db.Orders.Count(...); _count(id) in a loopNP0001covered: Reads/Accessors.cs
B9Direct and mutual recursionmethod queries and calls itself / another method that calls itNP0004covered: Reads/CallChains.cs, Reads/StoredAndShared.cs
B10Helpers, services, interface and virtual dispatchawait helper.Load(id) in a loopNP0001covered: Reads/CallChains.cs, Reads/Strategies.cs
B11Extension methodsc.OrderCount(db)NP0001covered: Reads/WorksCases.cs
B12Generic methodsCountOf<Order>() → db.Set<T>().Count()NP0001covered: Reads/WorksCases.cs
B13Default interface methodscounter.CountTwice(id) calling an implemented memberNP0001covered: Reads/WorksCases.cs
B14Property getter (not an entity) that queriesrow.OrderCount → db.Orders.Count(...)NP0001 (an assignment runs the setter only: silent)covered: Reads/Accessors.cs
B15Indexer that querieslookup[id] → db.Orders.First(...)NP0001covered: Reads/Accessors.cs
B16User-defined conversion reading navigationsOrderLine2 line = o; with implicit operator reading o.Customer.NameNP0002covered: Lazy/ImplicitReads.cs
B17Property patternsorders.Where(o => o is { Customer.Name: "vip" }), nested { Desk: { Floor: { ... } } }, switchNP0002covered: Lazy/ImplicitReads.cs
B18Null-conditional and switch readso.Customer?.Address?.CityNP0002covered: Lazy/WorksLazy.cs
B19Walking a lazy reference chainwhile (c.Parent != null) c = c.Parent;NP0002covered: Lazy/ImplicitReads.cs (the ground truth cannot drive the depth: the seeder builds two-level trees)
B20foreach with deconstruction, tuple items, key/value pairsforeach (var (id, o) in orders.Select(o => (o.Id, o))), pair.Order, foreach (var (id, o) in byId), kv.Value, orders.Index()NP0002 (silent when the part is an entity shared by every element)covered: Lazy/IndexedAndDeconstructed.cs
B21for loop over an EF list or array by indexfor (i...) orders[i].Customer.Name, backwards, through a localNP0002 (silent for an index that does not use the stepped counter)covered: Lazy/IndexedAndDeconstructed.cs
B22ToString() override reading navigations$"{desk}", Console.WriteLine(desk), string.Join(", ", desks), StringBuilder.Append, TextWriter.WriteNP0002covered: Lazy/ImplicitReads.cs
B23Record entity printed or comparedpublic record Ticket { virtual Agent Agent }; t.ToString() reads every navigationNP0002 when printed; silent when compared (the generated Equals/GetHashCode compare fields, measured)covered: Lazy/ImplicitReads.cs
B24Equals/GetHashCode overrides reading navigationsorders.Distinct(), new HashSet<Order>(orders), ToHashSet, Union, Intersect, ExceptNP0002covered: Lazy/ImplicitReads.cs
B25Events raised per elementProcessed?.Invoke(id) with a querying subscriberNP0001runtime guard (subscribers are decided at run time)
B26Elements passed to helpers, computed membersPrintCustomers(orders), invoices.Sum(i => i.Total)NP0002covered: Lazy/ComputedAndPassed.cs
B27Query fields and properties set in the constructor_big = db.Orders.Where(...)NP0001covered: Reads/StoredAndShared.cs
B28Element parts B20 does not followpairs.Sum(p => p.Order.Customer.Name.Length) (a part in a lambda), Zip, pairs built with new KeyValuePair(...), records built in Select (deconstructed or read by property), an index stepped inside a while loopNP0002covered: Lazy/MoreElementParts.cs
B29An override that queries and calls its base implementationpublic override async Task<BookDto> GetAsync(Guid id) { ...await repo.GetAsync(id)...; return await base.GetAsync(id); } (ABP application services)silent: base. runs the base body only, no recursioncovered: Reads/CallChains.cs (was a false NP0004 in abp-samples, fixed 2026-10-13)
B30A base call per element where only the base or only an override queriesforeach (var id in ids) total += await base.Price(id); (also through a helper), base.Label(order) reading navigations, await base.Orders().ToListAsync() when only the override includesNP0001 / NP0002 when the base body queries or reads; silent when only an override does (a call through the base type, _prices.Price(id), is dispatched and still reported)covered: Reads/CallChains.cs, Lazy/BaseCalls.cs (base calls were followed to every override: false NP0001 and NP0002, and a missed NP0002; fixed 2026-10-13)

C. ASP.NET Core and API layer​

#CaseSnippetExpectedStatusFix direction
C1Controller returns entitiesTask<List<Order>>, ActionResult<T>, Ok(orders), single entityNP0005covered: Endpoints/Endpoints.cs
C2CreatedAtAction, Created, Acceptedreturn CreatedAtAction(nameof(Get), new { id }, order)NP0005covered: Endpoints/WorksEndpoints.cs
C3Json(...), new OkObjectResult(...), new JsonResult(...)return Json(orders)NP0005covered: Endpoints/HubsAndViews.cs
C4IAsyncEnumerable<T> result=> db.Orders.AsAsyncEnumerable()NP0005covered: Endpoints/HubsAndViews.cs, Reads/Loops.cs (DbSet.AsAsyncEnumerable())
C5IQueryable<T> result (the serializer enumerates it)public IQueryable<Order> Get() => db.OrdersNP0005covered: Endpoints/WorksEndpoints.cs
C6Entities returned through a service=> await service.AllAsync()NP0005covered: Endpoints/WorksEndpoints.cs
C7Entities inside a result containernew Paged<Order>(await db.Orders.ToListAsync(), total), new Paged<Order> { Items = orders }NP0005covered: Endpoints/Containers.cs
C8Entities inside an anonymous objectOk(new { items = await db.Orders.ToListAsync() })NP0005covered: Endpoints/Containers.cs
C9Navigations marked [JsonIgnore][JsonIgnore] public virtual List<Book> Bookssilent (NP0005 for WhenWritingNull/WhenWritingDefault/WhenWriting, which read the value, and for the other serializer's attribute)covered: Endpoints/JsonIgnored.cs
C10Minimal API lambdaMapGet(..., async db => await db.Customers.ToListAsync()), TypedResults.Ok, Results.Ok, block bodiesNP0005covered: Endpoints/Endpoints.cs (Results.Ok, blocks: works)
C11Results.Json(data), TypedResults.Json, Json(data, options)Results.Json(await db.Orders.ToListAsync())NP0005covered: Endpoints/Endpoints.cs, Endpoints/HubsAndViews.cs
C12Minimal API method groups, MapGroupapi.MapGet("/orders", GetOrders)NP0005covered: Endpoints/HubsAndViews.cs
C13Query per element inside a handlerforeach (id in ids) await db.Orders.FindAsync(id)NP0001covered: Endpoints/WorksEndpoints.cs
C14View(...), PartialView(...)return View(await db.Orders.ToListAsync())silentcovered: Endpoints/HubsAndViews.cs
C15Razor views, Pages, view components, tag helpers reading navigations in markup@foreach (var o in Model) { @o.Customer.Name }NP0002runtime guard in the corpus; real builds include the Razor generator's output, so check with tools/scan.sh on an MVC appScan an MVC/Razor Pages app; follow Model from the View(...) call into the generated page if feasible
C16Blazor componentsmarkup loops; a row component querying in OnInitializedAsyncNP0001 / NP0002runtime guard (the component tree is built at render time)
C17SignalR SendAsync_hub.Clients.All.SendAsync("orders", orders)NP0005covered: Endpoints/Serialization.cs
C18Strongly typed hub clients, hub method resultsClients.Caller.OrdersChanged(orders), Task<List<Order>> GetOrders()NP0005covered: Endpoints/HubsAndViews.cs
C19Streaming hub methodsIAsyncEnumerable<Order> returned or yielded, ChannelReader<Order> whose channel the method writesNP0005covered: Endpoints/StreamingHub.cs (a writer passed to another method is not followed)
C20Explicit serializationJsonSerializer.Serialize(orders), SerializeAsyncNP0005covered: Endpoints/Serialization.cs
C21NewtonsoftJsonConvert.SerializeObject(orders); skips its own [JsonIgnore] and [IgnoreDataMember]NP0005covered: Endpoints/Serialization.cs, Endpoints/JsonIgnored.cs
C22gRPCreply.Orders.AddRange(orders.Select(o => new OrderMsg { Customer = o.Customer.Name }))NP0002covered by the generic rule: Lazy/LazyNavigation.cs
C23OData [EnableQuery] returning IQueryable[EnableQuery] public IQueryable<Order> Get() => db.Orders (on the action or its controller)silentcovered: Endpoints/OData.cs (the harness cannot run OData's formatter: those cases say why)
C24GraphQL resolvers per parent without a DataLoaderpublic Task<Customer> GetCustomer([Parent] Order o, AppDb db) => db.Customers.FindAsync(o.CustomerId)NP0001runtime guard (not planned before v1, see backlog)Later: treat Hot Chocolate type-extension resolvers taking [Parent] as per-element
C25Middleware, filters, health checks, hosted-service pollingone query per request or per pollsilentcovered: Reads/NotNPlusOne.cs, Reads/Strategies.cs
C26Validation attributes, IValidatableObject querying per bound item[ExistingProduct] public List<int> ProductIdsNP0001runtime guard (the framework invokes them by reflection)
C27Razor PageModel property assigned from a queryOrders = await db.Orders.ToListAsync() in OnGetAsyncsilent (the markup decides, C15)covered: Endpoints/WorksEndpoints.cs
C28MVC switched to Newtonsoft, navigations skipped with Newtonsoft's attributesAddNewtonsoftJson(); [Newtonsoft.Json.JsonIgnore] public virtual Customer Customer on a returned entitysilent (minimal APIs stay System.Text.Json)covered: EndpointsNewtonsoft/NewtonsoftMvc.cs

D. Libraries​

#CaseSnippetExpectedStatusFix direction
D1MediatR Send/Publish per element, also through helpersawait sender.Send(new GetCustomerTotal(id))NP0001covered: Reads/Mediator.cs
D2MediatR requests without a response, inherited Handle, Send(object) (through an object local)record ArchiveOrder(int Id) : IRequestNP0001 / silentcovered: Reads/MediatRVariants.cs
D3Mediator (source-generated): ICommand, IQuery, ValueTask handlersawait mediator.Send(new GetOrderCount(id))NP0001covered: Reads/MediatorLibrary.cs
D4MediatR pipeline behaviors that queryclass AuditBehavior<TReq, TRes>(AppDb db) : IPipelineBehavior<TReq, TRes> querying; Send in a loopNP0001 when the behavior is registered (typeof, AddBehavior, AddOpenBehavior) and its constraints accept the request; silent otherwisecovered: Reads/MediatRPipeline.cs (MediatR; the Mediator library's behaviors are not followed)
D5MediatR stream requestsawait foreach (var o in mediator.CreateStream(new StreamOrders(id))) in a loopNP0001covered: Reads/MediatRPipeline.cs
D6AutoMapper Map of a result or of each element; profiles, ForMember/MapFrom/Ignoremapper.Map<List<OrderView>>(orders)NP0002 / NP0007 / silentcovered: Mapping/MappingCases.cs
D7AutoMapper Map<S, D>, Map(src, dest), inline MapperConfiguration, ReverseMap, ForMember after other chained callsCreateMap<Order, OrderRow>().AfterMap(...).ForMember(d => d.CustomerName, o => o.Ignore())NP0002 / silentcovered: Mapping/RealApis.cs
D8ProjectTo (extension and IMapper.ProjectTo)db.Orders.ProjectTo<OrderView>(config)silentcovered: Mapping/MappingCases.cs, Mapping/RealApis.cs
D9AutoMapper ForPathForPath(d => d.Buyer.Name, o => o.MapFrom(s => s.Customer.Name)); ForPath(..., o => o.Ignore()) drops the root member's convention (measured)NP0002 / silentcovered: Mapping/AutoMapperMembers.cs
D10AutoMapper IncludeMembersCreateMap<Order, OrderNameView>().IncludeMembers(s => s.Customer)NP0002covered: Mapping/AutoMapperMembers.cs
D11AutoMapper value resolvers and converterso.MapFrom<BuyerResolver>() reading source.Customer; a resolver querying db; ConvertUsing<T>() / ConvertUsing(s => ...)NP0002 / NP0001covered: Mapping/AutoMapperMembers.cs
D12Map as a method grouporders.Select(mapper.Map<PathView>).ToList()NP0002covered: Mapping/AutoMapperMembers.cs
D13Mapster Adaptorders.Adapt<List<OrderView>>()NP0007covered: Mapping/MappingCases.cs
D14Mapster IMapper, TypeAdapterConfig, ProjectToTypemapsterMapper.Map<List<OrderView>>(orders)NP0007 / NP0002 / silentcovered: Mapping/RealApis.cs
D15Mapperly (source-generated mapper methods)[Mapper] partial class M { partial OrderView ToView(Order o); }NP0002covered: Mapping/Mapperly.cs (the corpus compilation runs Mapperly's generator)
D16FluentValidation RuleForEach(...).MustAsync queryingRuleForEach(x => x.ProductIds).MustAsync(async (id, ct) => await db.Products.AnyAsync(...))NP0001 (silent on RuleFor)covered: Reads/Validation.cs
D17Generic repository with an in-project implementationIRepository<T>.GetByIdAsync(id) → db.Set<T>().FindAsync(id)NP0001covered: Reads/WorksCases.cs
D18Unit of work wrapping SaveChangesawait uow.CommitAsync() in a loopNP0003covered: Writes/WorksWrites.cs
D19Repositories and specifications from a package (Ardalis.Specification)await repository.ListAsync(spec) in a loopNP0006covered by the rule: Reads/CallChains.cs (InterfaceWithoutImplementation)
D20Implementations in referenced projectsinterface implemented in the infrastructure projectNP0001covered: CrossProject/
D21Caching wrapperscache.GetOrCreateAsync(key, _ => db...) in a loopsilentcovered: Reads/WorksCases.cs (was a false positive, fixed 2026-10-12; a key per element is reported: the cache misses once per element)
D22Logging entitiesSerilog logger.Information("{@Order}", o) destructures every navigationNP0002runtime guard (the sink's destructuring decides)
D23Background jobs enqueued per elementBackgroundJob.Enqueue(() => Process(id))silentnot N+1 (job storage writes, not EF)
D24A repository from a package hands out its query in a taskvar q = await repository.GetQueryableAsync(); (ABP), GetDbSetAsync(), ValueTask<IQueryable<T>>; run per elementNP0001 (silent for a query of non-entities)covered: Reads/AsyncQuerySources.cs (was missed, fixed 2026-10-13)
D25ABP repository methods per elementawait authors.GetAsync(id), FindAsync, GetListAsync(b => ...), DeleteAsync(id), EnsureExistsAsync(id), EnsurePropertyLoadedAsync(b, x => x.Author), repository.FirstOrDefaultAsync(...)/CountAsync(...)NP0001covered: Abp/BookServices.cs (was missed, fixed 2026-10-13)
D26ABP AsyncExecuter on a repository's queryawait AsyncExecuter.FirstOrDefaultAsync(queryable.Where(...)) (an application service's, or an injected IAsyncQueryableExecuter)NP0001 (silent over an in-memory AsQueryable())covered: Abp/BookServices.cs (was missed, fixed 2026-10-13)
D27ABP writes per elementawait books.InsertAsync(book, autoSave: true), UpdateAsync(..., autoSave: true)NP0003 (silent without autoSave: the unit of work saves once)covered: Abp/BookServices.cs, AbpEfCore/Library.cs (was missed, fixed 2026-10-13)
D28ABP details and lazy loadingGetListAsync() then b.Author per element; includeDetails: true, WithDetails() (details the application configures), WithDetails(b => b.Author)NP0002 / silent / silentcovered: Abp/BookServices.cs, AbpEfCore/Library.cs (the configured details are not read: navigations count as loaded)
D29ABP application layer, which references no EF Coreentities are IEntity classes, repositories come injected or as CrudAppService.Repository; a custom repository interface implemented in the EF Core layerNP0001 / NP0006 for the custom interfacecovered: Abp/ (compiled without EF Core; was not analysed at all, fixed 2026-10-13)
D30ABP custom repositories (EfCoreRepository<,,>)(await GetDbSetAsync()).CountAsync(...), the base GetAsync(id), (await GetDbContextAsync()).Set<T>().FindAsync(id) per element; an override with a body of its own is followedNP0001 / silent (a cached override)covered: AbpEfCore/Library.cs (was missed, fixed 2026-10-13)

E. Reflection, dynamic dispatch, generated code​

#CaseSnippetExpectedStatusFix direction
E1Service resolved per element, then calledservices.GetRequiredService<IOrderCounter>().Count(id)NP0001covered: Reads/WorksCases.cs
E2Activator.CreateInstance cast to an interface((IOrderCounter)Activator.CreateInstance(...)).Count(id)NP0001covered: Reads/WorksCases.cs
E3Expression trees built at run timedb.Orders.Where(Expression.Lambda<Func<Order, bool>>(body, p)).ToList() in a loopNP0001covered: Reads/WorksCases.cs
E4MethodInfo.Invokemethod.Invoke(target, [id])NP0001runtime guard
E5dynamicdynamic t = ...; t.Count(id)NP0001runtime guard
E6Delegates injected from DIFunc<int, int> injected; injected(id)NP0001runtime guard
E7C# interceptorsa call redirected at compile time to a querying methodNP0001runtime guard (the analyzer sees the original target)
E8String-based Include, EF.PropertyInclude("Customer"), EF.Property<string>(o, "Note")silentcovered: Lazy/WorksLazy.cs, Reads/WorksCases.cs

Summary​

StatusCount
covered130
works (needs a corpus case)0
gap: missed0
gap: false positive0
runtime guard10
not N+13
unverified0 (A38's temporal operators stay unrun: SQL Server provider)
total143

Some rows count once under their main status even when part of the row is covered elsewhere.

Order for the gaps​

Every row closed from here gets its run-time proof with it: its corpus cases run in GroundTruthTests.

Precision first, then the patterns most common in real code, then the rare ones:

  1. C9 [JsonIgnore] navigations (done 2026-10-12, with C21), C23 OData [EnableQuery], C28 Newtonsoft MVC endpoints (done 2026-10-12).
  2. B21 for loop by index, B20 foreach deconstruction (done 2026-10-12; the rarer shapes are B28).
  3. C11 Results.Json, C7/C8 entities inside containers and anonymous objects (done 2026-10-12).
  4. D11 AutoMapper resolvers/converters, D10 IncludeMembers, D12 Map as a method group, D9 ForPath read explicitly (done 2026-10-12).
  5. A25/A26 client-evaluated final projections (done 2026-10-12).
  6. D4 MediatR pipeline behaviors, D5 stream requests, C19 streaming hub methods (done 2026-10-12).
  7. D16 FluentValidation RuleForEach (done 2026-10-12).
  8. B14/B15 property and indexer getters that query, B8/A19 delegate fields and compiled queries (done 2026-10-12).
  9. A37 derived-type navigations (done 2026-10-12).
  10. B22/B23/B24 ToString, record printers and equality reading navigations; B16 conversion operators; B17 property patterns; B19 lazy reference chains (done 2026-10-12).
  11. A23 transaction per element (done 2026-10-12).

Then turn every works row into a corpus case (both sides), and close the unverified rows by referencing the packages (Riok.Mapperly, Microsoft.AspNetCore.OData) in the test project. (done 2026-10-12: every row is covered, a runtime-guard row, or not N+1.)