A Roslyn analyzer that catches EF Core performance traps at compile time.
EF Core performance problems — N+1 queries, missing AsNoTracking(), unbounded queries, cartesian explosions, and synchronous database calls in async contexts — are among the most common .NET issues. EFQueryGuard detects these patterns during compilation with clear diagnostics and suggested fixes.
dotnet add package EFQueryGuardThat's it. The analyzer runs automatically during build with zero configuration.
| ID | Severity | Description |
|---|---|---|
| EFQ001 | Warning | Navigation property accessed in loop without Include() — causes N+1 queries |
| EFQ002 | Info | Read-only query missing AsNoTracking() |
| EFQ003 | Warning | Unbounded query without Take() or pagination |
| EFQ004 | Warning | Multiple collection Include() calls cause cartesian explosion |
| EFQ005 | Warning | Synchronous query method in async method |
| EFQ006 | Info | Expression may trigger client-side evaluation |
// Warning: Navigation 'Order.Items' accessed in loop without Include()foreach(varorderinctx.Orders.ToList()){varitems=order.Items;// N+1! Each iteration hits the database}// Fix: add Include()foreach(varorderinctx.Orders.Include(o =>o.Items).ToList()){varitems=order.Items;// OK — loaded eagerly}// Info: Read-only query on 'Products' missing AsNoTracking()varproducts=ctx.Products.Where(p =>p.Active).ToList();// Fix:varproducts=ctx.Products.AsNoTracking().Where(p =>p.Active).ToList();// Warning: Unbounded query on 'Products' — add Take() or paginationvarall=ctx.Products.ToList();// Fix:varpage=ctx.Products.Take(100).ToList();// Warning: Multiple Include() calls on 'Orders' may cause cartesian explosionvarorders=ctx.Orders.Include(o =>o.Items).Include(o =>o.Notes).ToList();// Fix: use AsSplitQuery()varorders=ctx.Orders.Include(o =>o.Items).Include(o =>o.Notes).AsSplitQuery().ToList();// Warning: Synchronous call 'ToList' in async method — use 'ToListAsync'publicasyncTask<List<Product>>GetProducts(TestContextctx){returnctx.Products.ToList();// blocks the thread!}// Fix:publicasyncTask<List<Product>>GetProducts(TestContextctx){returnawaitctx.Products.ToListAsync();}// Info: Call to 'IsValid' may trigger client-side evaluationvarproducts=ctx.Products.Where(p =>IsValid(p.Name)).ToList();// Fix: use inline expression or EF-translatable methodsvarproducts=ctx.Products.Where(p =>p.Name.Length>3).ToList();Suppress individual rules via .editorconfig:
[*.cs]dotnet_diagnostic.EFQ001.severity = none
dotnet_diagnostic.EFQ002.severity = warningOr suppress inline:
#pragma warning disable EFQ003varall=ctx.Products.ToList();
#pragma warning restore EFQ003EF Core 6, 7, 8, and 9. The analyzer detects patterns based on type names and method signatures, so it works across all supported versions.
- Fork the repository
- Create a feature branch
- Write tests for any new diagnostics
- Ensure
dotnet testpasses with zero failures - Submit a pull request
dotnet build # Build all
dotnet test# Run tests
dotnet build --no-incremental # Clean build
dotnet format # Format codeMIT