Entity Framework Core Performance: 10 Practical Tips for Faster .NET Apps
Slow pages are often slow queries. Ten EF Core techniques, from AsNoTracking and projections to split queries and compiled models, with code examples.

Entity Framework Core makes data access productive, but it is easy to write code that runs far more SQL than needed. Most performance problems in .NET business apps come from a few patterns. These ten tips fix the majority of them.
1. See the SQL
You cannot fix what you cannot see. Log generated SQL in development:
options.UseSqlServer(conn)
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging(); // development only
Look for many repeated queries and queries returning far more data than the page shows.
2. Use AsNoTracking for read-only queries
Change tracking costs memory and time. For lists and reports that you will not update:
var orders = await db.Orders.AsNoTracking()
.Where(o => o.Status == OrderStatus.Open)
.ToListAsync();
3. Project only what you need
Selecting entire entities with all columns is wasteful when the page needs three fields.
var rows = await db.Orders
.Where(o => o.CustomerId == id)
.Select(o => new { o.Id, o.Number, o.Total, CustomerName = o.Customer.Name })
.ToListAsync();
Projections also avoid loading related entities you do not need.
4. Avoid the N+1 problem
Loading a list and then accessing a navigation property for each item can produce one query per row, especially with lazy loading. Use Include or, better, a projection that gets everything in one query.
5. Use split queries for large includes
Including several collections in one query can multiply rows (a "cartesian explosion"). AsSplitQuery() loads each collection in a separate query:
var orders = await db.Orders
.Include(o => o.Lines)
.Include(o => o.Payments)
.AsSplitQuery()
.ToListAsync();
6. Paginate everything that can grow
Never load an entire table for a grid. Use Skip and Take with a stable order, or keyset pagination for very large tables:
var page = await db.Products.OrderBy(p => p.Id)
.Where(p => p.Id > lastId).Take(50).ToListAsync();
7. Add the right indexes
Queries filter and sort on columns that need indexes. Configure them in your model:
modelBuilder.Entity<Order>().HasIndex(o => new { o.CustomerId, o.CreatedAt });
Check the database's execution plans for slow queries and look for table scans.
8. Use bulk updates and deletes
Loading thousands of entities just to change one field is slow. ExecuteUpdate and ExecuteDelete run a single SQL statement:
await db.Products.Where(p => p.CategoryId == oldId)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.CategoryId, newId));
These bypass change tracking, so use them deliberately.
9. Keep the DbContext short-lived
Use one context per request (the default with dependency injection in ASP.NET Core). Long-lived contexts accumulate tracked entities and slow down. For high-throughput scenarios, consider DbContext pooling with AddDbContextPool.
10. Do work in the database, not in memory
Calling ToList() too early pulls data into memory before filtering:
// Bad: loads all orders, filters in memory
var big = db.Orders.ToList().Where(o => o.Total > 1000);
// Good: filter runs in SQL
var big2 = await db.Orders.Where(o => o.Total > 1000).ToListAsync();
Keep queries as IQueryable until the final step.
Quick reference
| Problem | Fix |
|---|---|
| Slow read-only lists | AsNoTracking, projections |
| Many repeated queries | Remove N+1 with projections or Include |
| Huge result sets | Pagination |
| Duplicate rows with many includes | AsSplitQuery |
| Slow filters and sorting | Indexes |
| Slow mass updates | ExecuteUpdate / ExecuteDelete |
| Memory growth | Short-lived contexts, pooling |
A real-world example
An ERP customer list page took more than eight seconds to load once the database grew past fifty thousand customers. Logging the SQL showed three problems: the page loaded every customer instead of one page of results, each row triggered an extra query to fetch the sales person's name, and the search filter on the customer name had no index. The fix was a projection that selected only the displayed columns including the sales person's name in one query, pagination of fifty rows, AsNoTracking, and a database index on the searched column. Load time dropped to well under half a second, with no change to the user interface. In most slow .NET pages, the cause is not the framework but a few avoidable queries.
Frequently asked questions
Should we use raw SQL or Dapper instead of EF Core for performance? For most queries, well-written EF Core is fast enough. For a few heavy reports or complex queries, raw SQL or Dapper can be a good complement. Measure before switching.
Do compiled queries help? EF Core caches query plans automatically. Explicit compiled queries help mainly in very hot paths executed many times per second. Start with the simpler tips above.
How do we find the slowest queries in production? Use database tools such as Query Store in SQL Server or pg_stat_statements in PostgreSQL, together with application performance monitoring, to find queries by total time and frequency.
Key takeaways
- Log SQL to see what EF Core really does.
- Use
AsNoTracking, projections and pagination for reads. - Eliminate N+1 queries, use split queries for big includes and add indexes.
- Use bulk operations and keep DbContext lifetimes short.
KHR Digital Solutions builds and tunes .NET applications with EF Core for ERP, POS and HR systems handling large data volumes.
Related articles

CI/CD for .NET with GitHub Actions: Build, Test and Deploy Automatically
A practical GitHub Actions pipeline for ASP.NET Core: restore, build, test, publish, and deploy safely with secrets and environment approvals.

Docker for .NET Developers: From First Container to Production Image
A practical walkthrough of containerising an ASP.NET Core app: multi-stage builds, small secure images, configuration, health checks and common mistakes.

ASP.NET Core Minimal APIs vs Controllers: An Architect's Guide for 2026
Beyond hello-world: how minimal APIs and controllers compare on structure, validation, filters, OpenAPI, versioning, testing and performance in large production codebases.