← Back to Blog .NET / Blazor

Entity Framework Core Performance Tuning for High-Traffic Applications

Saurav Rai

Founder & Lead Architect

· · 11 min read

Overview

Proven EF Core optimisations: split queries, projection, compiled queries, and avoiding N+1 pitfalls.

The N+1 Query Problem

The N+1 problem is the most common EF Core performance killer. It occurs when loading a collection with related data using lazy loading or separate queries for each parent.

The fix is eager loading with Include(), but be careful—cartesian explosion occurs when you include multiple collection navigations, multiplying result rows.

// BAD: N+1 queries
var orders = await ctx.Orders.ToListAsync();
foreach (var order in orders)
    var items = await ctx.OrderItems.Where(i => i.OrderId == order.Id).ToListAsync();

// GOOD: Single query with Include
var orders = await ctx.Orders.Include(o => o.Items).ToListAsync();

// BETTER: Use AsSplitQuery for multiple collections
var orders = await ctx.Orders
    .Include(o => o.Items)
    .Include(o => o.Shipments)
    .AsSplitQuery()
    .ToListAsync();

Projection Is Your Best Friend

Never load full entities when you only need a few columns. Projection via Select() generates an optimised SQL SELECT with only the required columns—reducing data transfer and memory pressure dramatically.

// BAD: Loads entire entity graph
var names = await ctx.Products.ToListAsync();
return names.Select(p => p.Name);

// GOOD: Projects only needed fields
var names = await ctx.Products.Select(p => p.Name).ToListAsync();

Compiled Queries for Hot Paths

LINQ queries are compiled to SQL on every execution. For high-traffic endpoints queried thousands of times per second, compiled queries cache the SQL translation—saving 2-5ms per call.

We apply compiled queries to the 20 most-called endpoints in production systems, typically seeing 15-30% reduction in p99 latency.

  • Use compiled queries for hot paths
  • Use AsSplitQuery for multiple collection includes
  • Always project with Select() in list endpoints
  • Use AsNoTracking() for read-only queries
  • Index columns used in Where() and OrderBy()

Key Takeaways

  • Always profile before optimising—use Azure Monitor or MiniProfiler
  • AsSplitQuery prevents cartesian explosion in complex includes
  • Projection can reduce query time by 10-100x on wide tables
  • Compiled queries eliminate repeated LINQ translation overhead
  • Connection pooling is critical under high concurrency

Saurav Rai

Founder & Lead Architect, Omni Stack

7+ years building enterprise .NET and cloud applications for clients across Australia, USA, and the Middle East. Passionate about clean architecture, developer experience, and shipping fast.

.NET / Blazor · 12 min read

Blazor Auto Render Mode: Server vs WebAssembly Per Component

.NET 8 per-component interactivity: our production guide to choosing Server vs WASM—with benchmarks.

Read More →
.NET / Blazor · 8 min read

Building Real-Time Dashboards with SignalR and Blazor Server

Step-by-step: live-updating dashboards using SignalR Hub groups, Blazor Server, and efficient diff rendering.

Read More →
Azure · 15 min read

Deploying .NET Microservices to Azure AKS with Helm and GitHub Actions

Complete CI/CD walkthrough: containerise .NET, write Helm charts, configure AKS, and automate deployment.

Read More →