Documentation

EF Core Analyzer Rules

A practical guide to LinqContraband's EF Core analyzer rules for query performance, loading, tracking, async execution, raw SQL safety, and CI policy.

EF Core Analyzer Rules

LinqContraband provides 63 EF Core analyzer rules for teams that want repeatable query review in the IDE and CI. The rules cover LINQ query shape, materialization, loading, async execution, tracking, bulk operations, schema modeling, and raw SQL safety.

Install the official analyzer package:

dotnet add package LinqContraband

Rule Families

Use these groups to decide which EF Core diagnostics are most useful for your project before diving into the full rule catalog.

Rule family Use it when you want to catch Starting rules
Query shape and translation Local methods, unstable ordering, non-translatable overloads, sorts that Distinct discards, query shapes that can fall out of SQL translation, and compiled queries that are compiled again on every call. EF Core client-side evaluation analyzer, LC001: local method, LC050: OrderBy before Distinct, LC061: compiled query not cached, EF Core pagination OrderBy analyzer
Materialization and projection Early ToList, whole-entity fetches, unbounded result sets, nested collection materializers, scalar reads that should project in SQL, and Min/Max/Average calls that throw when no row matches. EF Core projection analyzer, LC017: whole entity projection, LC057: Min, Max or Average on an empty query, EF Core premature materialization analyzer
Loading and includes Missing includes, cartesian explosion, excessive eager loading, deep include chains, includes that a projection ignores, and untagged complex queries. LC045: missing include, LC049: Include ignored by a projection, LC006: cartesian explosion, EF Core Include analyzer
Execution and async Database work inside loops, synchronous or overlapping same-context EF Core calls, repeated saves, missing cancellation tokens, async-stream buffering, TransactionScopes that do not flow across await, EF Core async operators on in-memory AsQueryable() queries, blocking on EF Core tasks with .Result or .Wait(), and transactions started outside a retrying execution strategy. LC046: concurrent DbContext operations, LC058: TransactionScope without async flow, LC060: async operator on an in-memory query, LC062: blocking on an EF Core async call, LC063: transaction under a retrying execution strategy, EF Core async query analyzer, EF Core CancellationToken analyzer
Tracking and context lifetime Missing AsNoTracking, no-tracking writes, mixed tracking modes, repeated SaveChanges, lost updates without a concurrency token, DbContext lifetime mistakes, and disposing the connection the context owns. LC030: DbContext lifetime, LC048: lost update risk, LC059: disposed context connection, LC036: context captured across threads, EF Core DbContext lifetime analyzer
Bulk operations and modeling Set-based write opportunities, unbounded bulk updates or deletes, tracked-delete pipeline bypasses, missing keys, missing explicit foreign keys, seed data or column defaults that change on every run, migrations that EF Core 9 rejects inside a transaction, and OnModelCreating overrides that skip the base configuration. LC047: ExecuteDelete bypasses tracked delete, LC054: Migrate inside a transaction, LC055: missing base.OnModelCreating, LC032: ExecuteUpdate, LC035: missing Where before bulk execute, EF Core ExecuteUpdate analyzer
Raw SQL and security Interpolated raw SQL, constructed SQL strings, unsafe command SQL, LINQ composed over stored procedure calls, and query-filter bypasses. LC018: interpolated raw SQL, LC034: interpolated command SQL, LC056: LINQ over a stored procedure, LC021: IgnoreQueryFilters

Rules To Enable First

For a low-noise rollout, start with the rules that usually represent clear production risk:

[*.cs]

# Repeated database work and missing related data
dotnet_diagnostic.LC007.severity = error
dotnet_diagnostic.LC045.severity = warning

# Raw SQL and query-filter bypasses
dotnet_diagnostic.LC018.severity = error
dotnet_diagnostic.LC034.severity = error
dotnet_diagnostic.LC037.severity = error
dotnet_diagnostic.LC021.severity = warning

# Expensive 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.LC031.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
dotnet_diagnostic.LC046.severity = warning

# Silent data loss
dotnet_diagnostic.LC044.severity = warning
dotnet_diagnostic.LC047.severity = warning
dotnet_diagnostic.LC048.severity = warning

Keep broader design guidance as warnings until the team has reviewed existing findings. Promote a rule to error only when it represents project policy and developers have a documented exception path.

Where Each Rule Fits