LinqContraband 5.8.1
See the version list below for details.
dotnet add package LinqContraband --version 5.8.1
NuGet\Install-Package LinqContraband -Version 5.8.1
<PackageReference Include="LinqContraband" Version="5.8.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="LinqContraband" Version="5.8.1" />
<PackageReference Include="LinqContraband"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add LinqContraband --version 5.8.1
#r "nuget: LinqContraband, 5.8.1"
#:package LinqContraband@5.8.1
#addin nuget:?package=LinqContraband&version=5.8.1
#tool nuget:?package=LinqContraband&version=5.8.1
LinqContraband
<div align="center">
![]()
Stop Smuggling Bad Queries into Production
</div>
Compile-time EF Core LINQ performance analyzer for .NET — a high-signal Roslyn analyzer that catches N+1 queries, client-side evaluation, premature materialization, sync-over-async, missing AsNoTracking, raw SQL injection risks, and other DbContext query issues in the editor and CI—not production.
The problem
Entity Framework Core and LINQ compile cleanly even when the query shape will hurt production. An N+1 Find inside a loop, ToList() before Where, a local method that forces client-side evaluation, sync-over-async on DbContext, or FromSqlRaw($"...") only fail under load, at 3 AM, or as a security incident.
Runtime profilers and code review miss what static analysis can prove from your IQueryable chains and EF Core API usage.
What it catches
LinqContraband reports proven EF Core LINQ and DbContext pitfalls early:
- N+1 database execution inside loops (
Find, materializers, explicit load) - premature materialization (
ToList/AsEnumerablebefore filters) - client-side evaluation risk from non-translatable local methods
- sync-over-async EF Core calls in async methods
- missing or misused AsNoTracking (tracking tax, silent writes, mixed modes)
- Cartesian explosion and missing/excessive Include paths
- SaveChanges inside loops and nested SaveChanges
- raw SQL injection patterns (
FromSqlRaw/ExecuteSqlRawinterpolation) - DbContext lifetime and concurrent same-context operations
- unbounded materialization, projection waste, and pagination without OrderBy
When the analyzer cannot prove an EF-backed query shape statically, it stays quiet. High-signal feedback, not noisy guesses.
Install
dotnet add package LinqContraband
That adds the latest release. To edit the project file by hand instead, use the version shown on the NuGet badge:
<PackageReference Include="LinqContraband" Version="x.y.z" PrivateAssets="all" />
No runtime dependency is added to your app. LinqContraband runs as a Roslyn analyzer during build and in supported IDEs (Visual Studio, Rider, VS Code / C# Dev Kit) and CI.
Install only from NuGet or from this repository. LinqContraband is not distributed as a standalone ZIP installer or executable; treat third-party ZIP downloads as untrusted.
- Official package: nuget.org/packages/LinqContraband
- Canonical source: github.com/georgepwall1991/LinqContraband
- Documentation: georgepwall1991.github.io/LinqContraband
A taste
// LC002: ToList() pulls every order into memory, then filters in C#.
var slow = db.Orders.ToList().Where(o => o.DueDate < today);
// Fix (offered as a code fix): filter in SQL, then materialize.
var fast = db.Orders.Where(o => o.DueDate < today).ToList();
// LC007: one database round trip per customer (N+1).
foreach (var id in customerIds)
customers.Add(db.Customers.Find(id));
// Fix: one query for the whole set.
customers = db.Customers.Where(c => customerIds.Contains(c.Id)).ToList();
Every diagnostic's help link in your IDE opens the rule's page, which shows the problem, the fix, and the cases the rule deliberately leaves alone.
See it work
Product-flow diagrams from real sample diagnostics and shipped LC message formats:
1. Build / IDE diagnostics (EF Core LINQ)
2. Before / after code fix (premature materialization)
3. Product loop — analyzer in IDE and CI
30-second path
- Reference the package with
PrivateAssets="all". - Keep writing EF Core LINQ as usual (
DbSet,IQueryable,Include,SaveChanges). - Build in the IDE or with
ContinuousIntegrationBuild=trueon the command line so analyzers run. - Fix any
LC00xwarnings (many have code fixes). - Optionally promote critical rules to error in
.editorconfig(see Configuration below).
Feature snapshot
| Area | What LinqContraband does |
|---|---|
| N+1 queries | Flags database execution inside loops and SaveChanges-in-loop write amplification. |
| Materialization | Catches premature ToList/AsEnumerable and redundant second materializers. |
| Translation | Reports local methods and non-translatable string/date patterns that risk client-side evaluation. |
| Tracking | Guides AsNoTracking, silent-write, and mixed tracking-mode hazards. |
| Loading | Detects Cartesian explosion, missing Include, deep ThenInclude, excessive eager loading. |
| Async | Sync-over-async, missing CancellationToken, async stream buffering, concurrent DbContext use. |
| Raw SQL | Interpolated FromSqlRaw/ExecuteSqlRaw and constructed SQL string risks. |
| Modeling | Missing primary keys and explicit foreign-key properties when statically provable. |
Compatibility
- .NET / Roslyn hosts: Visual Studio, Rider, VS Code (C# Dev Kit), and
dotnet build/ CI - EF Core: Modern Entity Framework Core versions used with C#
IQueryable/DbContextAPIs - Package kind: Development dependency analyzer (
PrivateAssets="all"); no app runtime package
Rules
48 rules, 31 with automatic code fixes. Each rule links to its full page: what it flags, why it matters, how to fix it, and where it deliberately stays quiet.
| Rule | What it catches | Default severity | Code fix |
|---|---|---|---|
| LC001 | Client-side evaluation risk: Local method usage in IQueryable | Warning | Yes |
| LC002 | Premature query continuation after materialization | Warning | Yes |
| LC003 | Prefer Any() over Count() existence checks | Warning | Yes |
| LC004 | Deferred Execution Leak: IQueryable passed as IEnumerable | Warning | Yes |
| LC005 | Multiple OrderBy calls | Warning | Yes |
| LC006 | Cartesian Explosion Risk: Multiple Collection Includes | Warning | Yes |
| LC007 | N+1 Problem: Database execution inside loop | Warning | Yes |
| LC008 | Sync-over-Async: Synchronous EF Core method in Async context | Warning | Yes |
| LC009 | Performance: Missing AsNoTracking() in Read-Only path | Info | Yes |
| LC010 | N+1 Write Problem: SaveChanges inside loop | Warning | Yes |
| LC011 | Design: Entity missing Primary Key | Warning | Yes |
| LC012 | Optimize: Use ExecuteDelete() instead of RemoveRange() | Warning | Yes |
| LC013 | Disposed Context Query | Warning | Manual |
| LC014 | Avoid String.ToLower() or ToUpper() in LINQ queries | Warning | Manual |
| LC015 | Deterministic Pagination: OrderBy required before Skip/Take | Warning | Yes |
| LC016 | Avoid DateTime.Now/UtcNow in LINQ queries | Warning | Yes |
| LC017 | Performance: Consider using Select() projection | Info | Yes |
| LC018 | Avoid FromSqlRaw with interpolated strings | Warning | Yes |
| LC019 | Conditional Include Expression | Warning | Manual |
| LC020 | Avoid untranslatable string comparison overloads | Warning | Yes |
| LC021 | Avoid IgnoreQueryFilters | Warning | Yes |
| LC022 | Nested collection materialization inside projection | Info | Yes |
| LC023 | Use Find/FindAsync for primary key lookups | Info | Yes |
| LC024 | GroupBy with Non-Translatable Projection | Warning | Manual |
| LC025 | Avoid AsNoTracking with Update/Remove | Warning | Yes |
| LC026 | Missing CancellationToken in async call | Info | Yes |
| LC027 | Missing Explicit Foreign Key Property | Info | Yes |
| LC028 | Deep ThenInclude Chain | Warning | Manual |
| LC029 | Redundant identity Select | Info | Yes |
| LC030 | Potential DbContext lifetime mismatch | Info | Manual |
| LC031 | Unbounded Query Materialization | Info | Manual |
| LC032 | Use ExecuteUpdate for provable bulk scalar updates | Info | Yes |
| LC033 | Use FrozenSet for provably read-only membership caches | Info | Yes |
| LC034 | Avoid ExecuteSqlRaw with interpolated strings | Warning | Yes |
| LC035 | Missing Where before bulk execute | Info | Manual |
| LC036 | DbContext captured by thread work item | Warning | Manual |
| LC037 | Avoid constructed raw SQL strings | Warning | Manual |
| LC038 | Avoid excessive eager loading | Info | Manual |
| LC039 | Avoid repeated SaveChanges on the same context | Info | Manual |
| LC040 | Avoid mixing tracking modes on the same context | Info | Manual |
| LC041 | Single entity query over-fetches one consumed property | Info | Yes |
| LC042 | Complex query should be tagged | Info | Manual |
| LC043 | Prefer await foreach over buffering async streams | Info | Yes |
| LC044 | AsNoTracking query mutated then SaveChanges — silent data loss | Warning | Manual |
| LC045 | Missing Include: navigation accessed on materialized entity | Warning | Yes |
| LC046 | Concurrent EF Core operations on the same DbContext | Warning | Manual |
| LC047 | ExecuteDelete bypasses the tracked delete pipeline | Warning | Yes |
| LC048 | Tracked update can overwrite a concurrent change | Warning | Manual |
Browse the same rules grouped by failure mode in the rule catalog, or start from a topic guide:
- N+1 query detector and SaveChanges in loops
- Client-side evaluation and premature materialization
- Include and eager loading and projection
- AsNoTracking and change tracking and DbContext lifetime
- Async queries and CancellationToken
- Raw SQL injection, ExecuteUpdate, and pagination OrderBy
- EF Core query performance checklist
Configuration
Set any rule's severity in .editorconfig:
[*.cs]
dotnet_diagnostic.LC001.severity = error
dotnet_diagnostic.LC002.severity = error
dotnet_diagnostic.LC003.severity = warning
# Optional rule-specific thresholds
dotnet_code_quality.LC038.include_threshold = 4
dotnet_code_quality.LC042.query_operator_threshold = 3
Advisory rules default to Info, so they show up as hints without drowning out the higher-confidence warnings.
Run it in CI
LinqContraband runs inside the normal dotnet build, so a CI job needs no database, service container, or extra tool:
- run: dotnet restore
- run: dotnet build --configuration Release --no-restore
Promote the rules you want to block pull requests to error in .editorconfig. The
CI guide covers a gradual rollout.
Contributing
Found a new way to smuggle bad queries? Open an issue or send a pull request. The contributing guide explains how to add or change a rule.
License: MIT
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.13.0 | 0 | 9/25/2026 |
| 5.12.0 | 0 | 9/24/2026 |
| 5.11.0 | 0 | 9/24/2026 |
| 5.10.0 | 82 | 9/23/2026 |
| 5.8.1 | 96 | 9/22/2026 |
| 5.8.0 | 1,520 | 9/10/2026 |
| 5.7.65 | 9,042 | 8/17/2026 |
| 5.7.64 | 116 | 8/17/2026 |
| 5.7.63 | 107 | 8/17/2026 |
| 5.7.62 | 114 | 8/17/2026 |
| 5.7.61 | 111 | 8/16/2026 |
| 5.7.60 | 111 | 8/16/2026 |
| 5.7.59 | 122 | 8/13/2026 |
| 5.7.58 | 110 | 8/10/2026 |
| 5.7.57 | 113 | 8/10/2026 |
| 5.7.56 | 113 | 8/10/2026 |
| 5.7.55 | 117 | 8/10/2026 |
| 5.7.54 | 110 | 8/10/2026 |
| 5.7.53 | 114 | 8/9/2026 |
| 5.7.52 | 102 | 8/9/2026 |
Analyzers no longer crash in the IDE (AD0001) when the DbContext, entity configuration, or a called helper lives in a referenced project: LC047 (and with it the LC012 tracked-delete gate) crashed at compilation start, and LC004, LC045, LC046, and LC048 had the same latent crash. LC044 now reports lost AsNoTracking mutations in async helpers completed through Task.WhenAll/WaitAll, stored tasks, and collection-expression combinators. Help links open the documentation site, and releases publish through NuGet Trusted Publishing.