EFCoreLint 0.2.0
dotnet add package EFCoreLint --version 0.2.0
NuGet\Install-Package EFCoreLint -Version 0.2.0
<PackageReference Include="EFCoreLint" Version="0.2.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="EFCoreLint" Version="0.2.0" />
<PackageReference Include="EFCoreLint"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add EFCoreLint --version 0.2.0
#r "nuget: EFCoreLint, 0.2.0"
#:package EFCoreLint@0.2.0
#addin nuget:?package=EFCoreLint&version=0.2.0
#tool nuget:?package=EFCoreLint&version=0.2.0
EFCoreLint
Roslyn analyzers that catch common Entity Framework Core anti-patterns at compile time — before they reach production.
Installation
dotnet add package EFCoreLint
No configuration required. Diagnostics appear automatically in your IDE and in dotnet build output.
Diagnostics
| ID | Severity | Description |
|---|---|---|
| EFLINT001 | Warning | Client-side filtering after materialization |
| EFLINT003 | Info | Read-only query missing AsNoTracking |
| EFLINT005 | Warning | EF Core async method called without await |
| EFLINT006 | Warning | Count() used for existence check instead of Any() |
| EFLINT007 | Warning | Blocking on EF Core async operation with .Result or .Wait() |
All severities can be overridden per-project via .editorconfig:
[*.cs]
dotnet_diagnostic.EFLINT001.severity = error
dotnet_diagnostic.EFLINT003.severity = none
EFLINT001: Client-side filtering
Calling ToList() or ToArray() before Where() pulls every row into memory and filters client-side. Move the filter before materialization so the database does the work.
// ❌ Warning EFLINT001 — loads all Products into memory first
var expensive = db.Products.ToList().Where(p => p.Price > 100);
// ✅ Fixed — WHERE clause executes on the database
var expensive = db.Products.Where(p => p.Price > 100).ToList();
A code fix is available: applying it rewrites the call order automatically.
EFLINT003: Missing AsNoTracking
EF Core tracks every queried entity by default, allocating a change-tracking snapshot even when you never call SaveChanges. For read-only queries, AsNoTracking() skips this overhead.
// ℹ️ Info EFLINT003 — change-tracking snapshot allocated unnecessarily
var products = db.Products.Where(p => p.IsActive).ToList();
// ✅ Fixed — no tracking overhead
var products = db.Products.AsNoTracking().Where(p => p.IsActive).ToList();
A code fix is available: it inserts .AsNoTracking() before the materializing call.
The diagnostic is suppressed when SaveChanges or SaveChangesAsync is called in the same method, because tracking is needed there.
EFLINT005: Missing await on async query
EF Core async methods (ToListAsync, FirstOrDefaultAsync, SaveChangesAsync, etc.) return a Task<T> that must be awaited to actually execute the query. Without await, the operation is silently discarded.
// ❌ Warning EFLINT005 — query never executes, result discarded
db.Products.ToListAsync();
// ✅ Fixed
var products = await db.Products.ToListAsync();
A code fix is available when the containing method is already async: it adds the await keyword.
EFLINT006: Use Any() instead of Count()
Calling Count() and comparing against 0 or 1 to check existence issues a COUNT(*) query that scans every matching row. Any() translates to EXISTS, which stops at the first match.
// ❌ Warning EFLINT006 — COUNT(*) scans all matching rows
if (db.Orders.Where(o => o.IsOpen).Count() > 0) { }
// ✅ Fixed — EXISTS short-circuits on the first match
if (db.Orders.Where(o => o.IsOpen).Any()) { }
Detected patterns: Count() > 0, Count() != 0, Count() >= 1, Count() == 0, Count() < 1, and their flipped equivalents. A code fix is available for all patterns, including Count(predicate) → Any(predicate).
EFLINT007: Blocking on async operation
Calling .Result, .Wait(), or .GetAwaiter().GetResult() on a Task returned by an EF Core async method blocks the calling thread and can deadlock in ASP.NET Core applications.
// ❌ Warning EFLINT007 — blocks the thread, risks deadlock
var products = db.Products.ToListAsync().Result;
// ✅ Fixed — asynchronous, no deadlock risk
var products = await db.Products.ToListAsync();
A code fix is available when the containing method is already async: it replaces the blocking call with await.
Suppressing a diagnostic
// In code
#pragma warning disable EFLINT001
var list = db.Products.ToList().Where(p => p.IsActive);
#pragma warning restore EFLINT001
// Or project-wide in .editorconfig
[*.cs]
dotnet_diagnostic.EFLINT001.severity = none
Contributing
Issues and pull requests welcome. Open an issue to discuss a bug or new diagnostic before submitting a PR.
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.