Friday, June 6, 2025

⚡ How to Optimize EF Core Queries for Better Performance






Published on: June 6, 2025

Category: Performance Tuning

Tags: Entity Framework Core, EF Core Performance, .NET Optimization, Query Tuning, LINQ, Database Best Practices




🚨 Common EF Core Pitfalls

Before jumping into optimizations, let’s identify common mistakes:

  • Overusing .Include() for relationships that aren't needed
  • Fetching more data than necessary (wide SELECTs)
  • Not using .AsNoTracking() for read-only queries
  • Making multiple small queries instead of batching


✅ 1. Use .AsNoTracking() When You Don’t Need to Track

var users = await context.Users
    .AsNoTracking()
    .Where(u => u.IsActive)
    .ToListAsync();

✅ This can improve read performance by up to 50% in large datasets because EF skips change tracking.



✅ 2. Avoid N+1 Problems with Proper .Include()

// Good: eager load related data when needed
var orders = await context.Orders
    .Include(o => o.Customer)
    .Include(o => o.Items)
    .ToListAsync();

🧠 Only include what you need. Too many .Include()s can bloat query size and response time.



✅ 3. Use Projection Instead of Loading Entire Entities

var summaries = await context.Orders
    .Where(o => o.OrderDate > DateTime.UtcNow.AddDays(-30))
    .Select(o => new {
        o.Id,
        o.Total,
        o.Status
    })
    .ToListAsync();

✅ Load only the columns you need — this reduces memory use and speeds up serialization.



✅ 4. Filter Early, Not Late

// Good
var filtered = await context.Products
    .Where(p => p.IsAvailable && p.Price < 100)
    .ToListAsync();

⛔ Avoid applying filters in memory after .ToList(). Always push filters to the database.



✅ 5. Use Compiled Queries (Advanced Use)

static readonly Func<AppDbContext, int, Task<User?>> GetUserById =
    EF.CompileAsyncQuery((AppDbContext ctx, int id) =>
        ctx.Users.FirstOrDefault(u => u.Id == id));


🧠 Bonus Tips

  • Index columns used in filters and joins
  • Monitor SQL logs via ToQueryString() or ILogger
  • Use FirstOrDefaultAsync or AnyAsync instead of Count() when checking existence
  • Avoid loading child collections if you're not using them


📊 Tools to Monitor EF Core Performance

  • EF Profiler
  • MiniProfiler
  • SQL Server Query Store
  • Application Insights (Azure)



📬 Stay Connected