Breaking / LiveLIVE
DEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AIDEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AI

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.

By KHR Editorial Team··3 min read
Entity Framework Core Performance: 10 Practical Tips for Faster .NET Apps

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.

Entity Framework Core Performance: 10 Practical Tips for Faster .NET A | KHR Tech Blog