Entity Framework Core makes database interactions feel natural in C#, but that convenience hides real performance costs. The most damaging bugs are silent: queries that execute hundreds of times when one would suffice. This guide covers the patterns that actually perform in production.
The N+1 Problem and Eager Loading
This query fires one query for orders, then one query per order to fetch customers—dangerous at scale:
// GOOD: Single UPDATE statement await_db.Items .Where(i=>i.CreatedBefore<DateTime.UtcNow.AddYears(-1)) .ExecuteUpdateAsync(s=>s.SetProperty(i=>i.Status,"archived"));
Disable Tracking for Read-Only Queries
EF Core tracks every entity returned so it can detect changes on SaveChanges(). For read-only queries, this is wasted memory and CPU:
varorders=await_db.Orders .AsNoTracking()// Tells EF not to track changes .ToListAsync();
Optimizing database queries is half the battle—the other half is knowing which patterns scale. This is the kind of architecture work I help teams tackle as part of custom software development engagements where performance matters.
Common questions
Why is lazy loading dangerous in EF Core?
Lazy loading executes a database query whenever you access a related entity that wasn't explicitly loaded. In a loop, this causes N+1 queries—one for the parent, then one for each child. Use Include() or Select projection instead.
Should I disable lazy loading entirely?
Many teams do, to force explicit eager loading and catch N+1 bugs during development. You can set LazyLoadingEnabled = false in OnConfiguring(). This makes performance issues visible immediately rather than as surprises in production.
Is AsNoTracking a silver bullet?
For read-only queries, yes—it reduces memory and CPU overhead significantly. But don't use it on entities you plan to modify; EF Core can't track changes on untracked entities, so SaveChanges() won't persist your edits.
What's the difference between Skip/Take and LIMIT/OFFSET?
They're the same thing. Skip(n).Take(m) generates SQL's OFFSET n ROWS FETCH NEXT m ROWS ONLY. It's database-efficient and keeps your query result sets bounded regardless of total data size.
Struggling with slow database queries?
I help teams identify and eliminate performance bottlenecks in their EF Core applications.