Entity Framework Core (EF Core) performance optimization is about reducing database load, minimizing memory usage, and improving query execution time while keeping your code maintainable.
Below is a practical, developer-focused guide covering core components, best practices, examples, and alternatives.
What affects EF Core performance?
At a high level, performance depends on:
• Query generation (LINQ → SQL translation)
• Database round-trips
• Change tracking
• Data size (over-fetching)
• Index usage in the database
• Context lifetime and configuration
Required / Key Components
1. DbContext configuration
Controls tracking, pooling, logging, etc.
options.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
2. Database provider
EF Core performance depends heavily on provider:
• SQL Server
• PostgreSQL
• SQLite
Each has different query capabilities and optimizations.
3. Indexes (database-level)
EF Core doesn’t replace proper indexing.
modelBuilder.Entity()
.HasIndex(u => u.Email);
4. Logging & profiling tools
To detect slow queries:
• EnableSensitiveDataLogging()
• SQL logs
• Database execution plans
EF Core Best Practices
1. Use AsNoTracking() for read-only queries
Tracking adds overhead.
var users = await context.Users
.AsNoTracking()
.ToListAsync();
Use when you don’t update entities
2. Select only what you need
Avoid loading full entities unnecessarily.
Bad practice:
var users = await context.Users.ToListAsync();
Good practice:
var users = await context.Users
.Select(u => new { u.Id, u.Name })
.ToListAsync();
3. Avoid N+1 queries (use eager loading)
Bad practice:
var orders = context.Orders.ToList();
foreach (var order in orders)
{
var items = context.OrderItems
.Where(i => i.OrderId == order.Id)
.ToList();
}
Good practice:
var orders = context.Orders
.Include(o => o.Items)
.ToList();
4. Use compiled queries for hot paths
Reduces LINQ translation overhead.
static readonly Func<MyContext, int, Task> _getUserById =
EF.CompileAsyncQuery((MyContext ctx, int id) =>
ctx.Users.FirstOrDefault(u => u.Id == id));
5. Use pagination (Skip / Take)
Never load large datasets at once.
var users = await context.Users
.OrderBy(u => u.Id)
.Skip(100)
.Take(50)
.ToListAsync();
6. Disable lazy loading (usually)
Lazy loading often causes hidden queries.
// Avoid enabling proxies unless necessary
7. Batch updates instead of looping
Bad practice:
foreach (var user in users)
{
user.IsActive = false;
}
await context.SaveChangesAsync();
Better practice (EF Core 7+):
await context.Users
.Where(u => u.LastLogin < DateTime.Now.AddYears(-1))
.ExecuteUpdateAsync(u => u.SetProperty(x => x.IsActive, false));
8. Use connection pooling / DbContext pooling
services.AddDbContextPool(options =>
options.UseSqlServer(connectionString));
9. Optimize SaveChanges
Use fewer calls
Wrap in transactions when needed
10. Use proper data types
Avoid mismatches (e.g., string vs int keys)
Example: Optimized Query
var result = await context.Orders
.AsNoTracking()
.Where(o => o.Status == "Completed")
.OrderByDescending(o => o.CreatedAt)
.Select(o => new
{
o.Id,
o.Total,
CustomerName = o.Customer.Name
})
.Take(20)
.ToListAsync();
• No tracking
• Projection
• Limited result set
• Efficient SQL
Common Performance Mistakes
• Loading entire tables into memory
• Ignoring indexes
• Overusing Include
• Using synchronous calls (ToList() instead of ToListAsync)
• Keeping DbContext alive too long
• Multiple SaveChanges() in loops
Alternatives to EF Core
Depending on your performance needs:
1. Dapper
• Very fast (close to raw SQL)
• Less abstraction
• Best for read-heavy scenarios
2. Raw ADO.NET
• Maximum control
• More boilerplate
3. NHibernate
• Mature ORM with advanced caching
• More complex than EF Core
4. Hybrid approach
• EF Core for writes
• Dapper for reads
When EF Core is enough vs not?
EF Core is great when:
• You want productivity + maintainability
• Medium-scale applications
• Complex domain models
Consider alternatives when:
• Ultra-high throughput (millions of queries/sec)
• Tight latency requirements
• Heavy reporting queries
Advanced Techniques
• Second-level caching libraries
• Split queries (AsSplitQuery)
• Query tags for debugging
• Database-side stored procedures