Documentation
EF Core Query Performance Checklist
A practical EF Core query performance checklist for reviewing LINQ queries, N+1 risks, raw SQL safety, tracking modes, and CI enforcement with LinqContraband.
EF Core Query Performance Checklist
Use this EF Core query performance checklist when reviewing pull requests, tightening team standards, or adding compile-time query analysis to CI. It maps common review questions to LinqContraband rules so teams can move repeatable feedback out of human memory and into analyzer diagnostics.
Install the official analyzer package:
dotnet add package LinqContraband
Pull Request Checklist
| Check | Why it matters | LinqContraband signal |
|---|---|---|
| Avoid database calls inside loops. | Loop execution can turn one request into many database roundtrips. | LC007: database execution inside loop |
Batch SaveChanges outside loops unless each item needs its own commit boundary. |
Repeated saves can turn one unit of work into many transactions and partial-progress states. | LC010: SaveChanges inside loop |
| Keep query logic translatable to SQL. | Local helpers, provider-sensitive string overloads, and complex grouping can fail translation or push work into memory. | LC001: local method, EF Core client-side evaluation analyzer |
| Order before pagination or positional access. | Unordered Skip, Take, Last, ElementAt, or Chunk queries can return unstable rows. |
LC015: missing OrderBy, EF Core pagination OrderBy analyzer |
| Materialize only after filtering, ordering, and projection. | Early ToList, AsEnumerable, or ToArray can move work from SQL into memory. |
LC002: premature materialization, EF Core premature materialization analyzer |
| Use projection when only a few fields are needed. | Loading whole entities increases network, memory, and tracking cost. | LC017: whole entity projection, LC041: single entity scalar projection, EF Core projection analyzer |
| Make related data loading explicit. | Missing includes can produce null navigation data, lazy-loading churn, or hidden N+1 behaviour. | LC045: missing include |
| Keep eager loading bounded. | Overusing Include can create cartesian explosion or very wide result graphs. |
LC006: cartesian explosion, LC038: excessive eager loading |
| Prefer async EF Core APIs in async methods. | Synchronous EF Core calls in async paths block request threads. | LC008: sync-over-async, EF Core async query analyzer |
| Pass cancellation tokens through async query APIs. | Long-running queries should respect request cancellation and shutdown paths. | LC026: missing cancellation token, EF Core CancellationToken analyzer |
| Keep DbContext lifetimes scoped and single-threaded. | Long-lived or cross-thread contexts can leak tracked state, cross request boundaries, or throw at runtime. | LC030: DbContext lifetime mismatch, LC036: DbContext captured across threads, EF Core DbContext lifetime analyzer |
| Use read-only tracking intentionally. | Tracking every read increases memory and can create confusing mixed-mode behaviour. | LC009: missing AsNoTracking, LC040: mixed tracking modes |
| Keep raw SQL parameterized. | Interpolation and string construction can turn EF Core raw SQL into injection risk. | LC018: interpolated raw SQL, LC034: interpolated command SQL, LC037: constructed raw SQL strings |
| Review global filter bypasses. | IgnoreQueryFilters can skip tenant, soft-delete, or security boundaries. |
LC021: IgnoreQueryFilters |
| Bound destructive set-based writes. | Bulk delete/update without a filter can affect more rows than intended. | LC035: missing Where before bulk execute |
| Prefer reviewed set-based writes for large batches. | Tracked loops and RemoveRange can load rows that EF Core can update or delete directly in SQL. |
LC032: ExecuteUpdate, LC012: ExecuteDelete over RemoveRange |
| Tag complex queries before they reach production. | Query tags make expensive SQL easier to trace in database telemetry. | LC042: missing query tags |
Quick Severity Policy
Start with analyzer warnings, then promote a focused rule set to errors once the team has cleared existing findings:
[*.cs]
# Expensive or risky query shapes
dotnet_diagnostic.LC001.severity = warning
dotnet_diagnostic.LC002.severity = warning
dotnet_diagnostic.LC014.severity = warning
dotnet_diagnostic.LC015.severity = warning
dotnet_diagnostic.LC020.severity = warning
dotnet_diagnostic.LC024.severity = warning
dotnet_diagnostic.LC007.severity = error
dotnet_diagnostic.LC045.severity = warning
dotnet_diagnostic.LC036.severity = warning
# Async EF Core execution
dotnet_diagnostic.LC008.severity = warning
dotnet_diagnostic.LC026.severity = suggestion
dotnet_diagnostic.LC043.severity = suggestion
# Raw SQL and security-sensitive paths
dotnet_diagnostic.LC018.severity = error
dotnet_diagnostic.LC034.severity = error
dotnet_diagnostic.LC037.severity = error
dotnet_diagnostic.LC021.severity = warning
Use project-wide error severities for rules that represent team policy. Use narrow pragmas or SuppressMessage
attributes only when a reviewer has accepted a specific exception.
How To Use This Checklist
- Link it from pull request templates, engineering handbooks, and onboarding docs.
- Pair it with the EF Core analyzer rules guide when a team needs the broader diagnostic map before choosing severities.
- Pair it with the EF Core query analyzer CI guide so the highest-risk checklist items become automated build feedback.
- Use the EF Core client-side evaluation analyzer guide when
local methods,
AsEnumerableboundaries,StringComparisonoverloads, or non-translatableGroupByprojections need focused guidance. - Use the EF Core pagination OrderBy analyzer guide when
unordered
Skip,Take,Last,ElementAt,Chunk, or misplacedOrderBycalls need focused guidance. - Use the EF Core DbContext lifetime analyzer guide when singleton contexts, background work, cross-thread capture, disposed queries, or tracking-mode surprises need focused guidance.
- Use the EF Core async query analyzer guide when
ToListAsync, sync-over-async, missing cancellation tokens, async stream buffering, orSaveChangesAsyncloops need focused guidance. - Use the EF Core CancellationToken analyzer guide when
missing tokens on
ToListAsync,FirstOrDefaultAsync,SaveChangesAsync, or similar async EF Core calls need a focused rollout page. - Use the EF Core premature materialization analyzer guide
when early
ToList,AsEnumerable, unbounded materialization, or projection waste need focused guidance. - Use the EF Core projection analyzer guide when whole-entity loads,
scalar reads, nested collection materializers, or redundant identity
Selectcalls need focused guidance. - Send focused topics to the EF Core Include analyzer guide, the EF Core N+1 query detector guide, and the EF Core raw SQL injection analyzer guide when reviewers need deeper examples.
- Use the EF Core AsNoTracking analyzer guide when read-only query tracking, mixed tracking modes, or silent no-tracking writes need focused guidance.
- Use the EF Core ExecuteUpdate analyzer guide when bulk update,
bulk delete, or missing
Wherebefore set-based writes need focused guidance. - Use the EF Core SaveChanges in loop analyzer guide when repeated saves, N+1 writes, or batched write policy need focused guidance.
- Use the full rule catalog to tune the exact severity policy for your application.
Official Links
- Canonical repository: github.com/georgepwall1991/LinqContraband
- Official NuGet package: nuget.org/packages/LinqContraband
- Documentation hub: georgepwall1991.github.io/LinqContraband
- Safe install guidance: Official LinqContraband downloads and authenticity