DEV Community

Abhinaw
Abhinaw

Posted on • Originally published at bytecrafted.dev

11 EF Core Performance Mistakes That Will Kill Your App

Your app works fine in development with 100 test records. Then production hits with real data, and suddenly everything crawls. Sound familiar?

I've debugged enough slow .NET apps to know the real culprit: bad EF Core queries. Here are the 11 most common mistakes I see in production code.

For detailed code examples and performance benchmarks, check out the full deep-dive guide.

1. The N+1 Query Problem

Bad: Load orders, then access order.Customer.Name in a loop = 1,001 database calls
Fix: Use Include(o => o.Customer) or projection with Select()

I've written a detailed breakdown of N+1 queries and Include vs SplitQuery if you want to dive deeper.

2. Premature ToList()

Bad: _context.Products.ToList().Where(p => p.Price > 100) fetches everything first
Fix: Build your query completely, then call ToList() once

3. Client-Side Evaluation

Bad: Using Func<T, bool> forces filtering in memory after downloading all data
Fix: Use Expression<Func<T, bool>> to keep filtering in SQL

4. Over-Fetching Data

Bad: Loading entire entities when you only need Name and Price
Fix: Use projection: Select(p => new { p.Name, p.Price })

5. Missing Indexes

Bad: Querying without indexes = full table scans
Fix: Add indexes for columns in WHERE, ORDER BY, and JOIN clauses

6. Unnecessary Change Tracking

Bad: EF Core tracks entities you'll never update, wasting memory
Fix: Use AsNoTracking() for read-only queries

7. Lazy Loading Surprises

Bad: Accessing navigation properties triggers hidden database calls
Fix: Explicitly load related data with Include() or turn lazy loading off

8. Poor DbContext Lifetime

Bad: Creating new contexts for every operation or keeping one alive forever
Fix: Use scoped lifetime in DI - one context per web request

9. Non-Compiled Repeated Queries

Bad: EF Core rebuilds query plans for identical queries
Fix: Use EF.CompileAsyncQuery() for frequently executed queries

10. Individual Operations

Bad: Calling SaveChanges() in a loop
Fix: Add all entities, then call SaveChanges() once

11. No Performance Monitoring

Bad: Flying blind without seeing generated SQL
Fix: Enable query logging and use ToQueryString() for debugging

The Real Cost

I've seen these mistakes turn 50ms queries into 5-second nightmares. One N+1 problem in production generated 2,000+ database calls for a single page load.

Quick Wins

  • Add AsNoTracking() to read-only queries (instant memory savings)
  • Use projection instead of loading full entities
  • Check your logs for repeated similar queries (N+1 red flag)
  • Add indexes for your most common WHERE clauses

Debug Like a Pro

Enable query logging in development:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Use ToQueryString() to see what SQL EF Core generates before executing queries.

Bottom Line

Good EF Core performance isn't about tricks - it's about respecting what database operations actually cost. When you understand that every navigation property access might trigger a database call, you'll naturally write better code.

Most performance problems aren't in your business logic or UI. They're in those 5 lines of innocent-looking EF Core queries that seemed fine with test data.

Want the complete guide? I've written a detailed breakdown with full code examples, SQL output, and performance metrics at my post..

What EF Core performance issues have you run into? Drop a comment below.

Top comments (0)