Entity Framework Core performance best practices, diagnostics, modeling, and advanced optimization techniques. Use when writing, reviewing, or optimizing EF Core data access code.
Optimize EF Core applications by following official performance best practices, from modeling to runtime execution.
Reference: EF Core Performance Documentation
BenchmarkDotNet for benchmarking. Don't optimize without data.Capture command execution times using LogTo (simple logging) or ILoggerFactory. Use Query Tags to correlate LINQ queries with SQL logs.
var results = await context.Blogs
.TagWith("SearchBlogsByRating")
.Where(b => b.Rating > 3)
.ToListAsync();
Identify slow queries and analyze their Execution Plans using database tools (e.g., SQL Server Management Studio, Azure Data Studio). Look for missing indexes and costly joins.
Monitor Microsoft.EntityFrameworkCore metrics:
Always use .Select() to project only required properties. Avoid fetching entire entities for read-only operations.
var names = await context.Blogs.Select(b => b.Name).ToListAsync();
For read-only operations, use .AsNoTracking() to avoid the overhead of change tracking and identity resolution.
var blogs = await context.Blogs.AsNoTracking().ToListAsync();
.Include() for data you know you'll need..AsSplitQuery() to avoid "Cartesian Explosion" in 1:N joins.Use ExecuteUpdate and ExecuteDelete for mass updates without loading entities into memory.
await context.Employees
.Where(e => e.Salary < 50000)
.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));
EF Core automatically batches multiple changes into a single roundtrip on SaveChanges. Avoid calling SaveChanges inside loops.
Reduces context setup overhead in high-traffic applications.
builder.Services.AddDbContextPool<MyDbContext>(o => o.UseSqlServer(connectionString));
Pre-compile LINQ queries into delegates for absolute maximum performance in hot paths.
private static readonly Func<MyDbContext, int, Task<Blog?>> _getBlogById =
EF.CompileAsyncQuery((MyDbContext context, int id) => context.Blogs.FirstOrDefault(b => b.Id == id));
For startup performance, enable NativeAOT and use query precompilation via interceptors.
Microsoft.EntityFrameworkCore.Tasks package.dotnet publish to statically identify queries and generate interceptors..Select() to limit fetched columns?.AsNoTracking()?AsSplitQuery() used for multiple 1:N joins?ExecuteUpdate/ExecuteDelete?TagWith and Execution Plans?