Updated Published 9 min read
EF Core and SQL Server Performance
EF Core and SQL Server performance: N+1 queries, projections, AsNoTracking, indexes, and reporting pain that only shows under real data volume.
Blog topic
Article map for EF Core on ASP.NET Core — links to the performance checklist, N+1, cartesian explosion, relationships, and SQL Server plans.
Start with the EF Core performance checklist, then open the failure-mode article that matches your symptom.
This page is the article map for EF Core on ASP.NET Core. Open the symptom-specific post for deep answers — this index does not replace them.
| You want… | Open |
|---|---|
| Performance checklist | EF Core SQL performance |
| N+1 / Include / AsSplitQuery | N+1 guide |
| Cartesian explosion | Two collection Includes |
| Relationships 1-1 / 1-n / n-n | EF relationships |
| AsNoTracking identity | AsNoTracking |
| Parameter sniffing | Parameter sniffing |
| Migrations in production | EF Core migrations |
| Audit log interceptors | SaveChanges interceptors |
| Value conversions / enums | Value conversions |
| Bulk update / ExecuteUpdate | Bulk update |
| Optimistic concurrency | Concurrency tokens |
| Soft delete / global filters | Global query filters |
| Specification pattern | Specification pattern |
| IEnumerable vs IQueryable | IEnumerable vs IQueryable |
| Deadlocks / RCSI | Deadlocks and snapshot isolation |
| TempDB contention | TempDB contention |
| Interview scenarios | EF Core interview questions |
EF Core translates LINQ to SQL, tracks entities in a scoped DbContext, and materializes results. Most production pain falls into three categories:
Query shape problems: Too many round-trips (N+1), too many rows returned (missing filter, no paging), too many columns (SELECT * where a projection suffices). Start with the performance checklist.
Concurrency problems: Two requests updating the same row without a concurrency token cause silent last-write-wins data loss. Reader/writer deadlocks under load are usually fixed by enabling RCSI at the database level. See optimistic concurrency and deadlocks.
Schema evolution problems: Database.Migrate() on startup races when multiple pods boot simultaneously. A non-nullable column with a default can lock a table for minutes. The expand-and-contract pattern avoids downtime. See migrations in production.
The most common EF Core mistake is declaring a repository return type as IEnumerable<T> when the underlying query is IQueryable<T>. The caller adds .OrderBy(id).Take(20) — those operators run in memory, after loading every row. The fix is to keep IQueryable<T> until you have applied all filters and paging, then materialize once with ToListAsync.
See IEnumerable vs IQueryable and LINQ interview questions for the full set of patterns interviewers probe.
DbContext per request. Never share across threads.AsNoTracking for read-only queries. Removes change tracking overhead. Does not reduce SQL round-trips.ExecuteUpdateAsync bypasses interceptors. If your audit trail uses ISaveChangesInterceptor, bulk updates via ExecuteUpdate will not be stamped — set the audit columns in SetProperty explicitly.Up() method. Rename = drop + add unless you edit the migration. Non-nullable column with default = potential table lock on large tables.PAGELATCH_UP waits — the API hangs while CPU looks idle.Diagnosing a slow query? Start with SQL performance. Debugging a deadlock? Read deadlocks and snapshot isolation. Preparing for an interview? Go to EF Core interview questions, then LINQ interview questions.
For the broader .NET architecture picture — where EF Core fits into Clean Architecture and CQRS — see the architecture hub.
Related: Interview Questions · Async & Threading
Checklist first: EF Core and SQL Server performance. This page indexes the deeper failure modes.
EF Core relationships and AsNoTracking vs identity resolution.
Updated Published 9 min read
EF Core and SQL Server performance: N+1 queries, projections, AsNoTracking, indexes, and reporting pain that only shows under real data volume.
Updated Published 8 min read
Map one-to-one, one-to-many, and many-to-many in EF Core — owned types vs FK, skip-level n-n vs a join entity with payload, delete behavior, and the Angular DTOs I actually return.
Updated Published 5 min read
EF Core N+1 problem: extra SQL round-trips when a loop touches navigation properties. How Include, projection, and AsSplitQuery each fix a different bug.
Updated Published 6 min read
Cartesian explosion in EF Core: two collection Includes multiply SQL rows (lines × events). How to confirm in SSMS and fix with split query or fewer Includes.
Updated Published 9 min read
EF Core interview questions with production answers — entity relationships, RowVersion concurrency, global query filters, ExecuteUpdate vs SaveChanges, and tenant leaks.
7 min read
Implement DDD value objects in .NET as C# records. Map with EF Core using OwnsOne for owned entities and HasConversion for primitive properties.
8 min read
Sharding strategies and shard key selection for SQL Server in .NET microservices — range, hash, and directory sharding explained with examples.
10 min read
Database interview prep for .NET developers. How to answer questions on sharding, replication, indexing, ACID, BASE, and SQL vs NoSQL.
11 min read
EF Core connection pooling is the SqlClient pool, not AddDbContextPool. Dispose the context to return the session before raising Max Pool Size.
10 min read
EF Core aggregate design for multi-tenant SaaS: one root per save, TenantId stamped by that root, and no second aggregate in the transaction.
4 min read
Publish a message only if the EF Core save committed. The transactional outbox writes the event in the same transaction as the order, then a background worker sends it.
5 min read
IEnumerable vs IQueryable vs ICollection in EF Core: which one still builds SQL, which one loads the table, and which one is only a navigation collection.
4 min read
The ASP.NET Core error 'A second operation was started on this context' means one DbContext is running two queries. The usual causes are a missing await, a parallel task, or a context stored in a singleton.
4 min read
How to run EF Core migrations in production without a startup race: generate a SQL script, apply it once in the pipeline, and use expand and contract when the app is already live.
Updated Published 5 min read
Master EF Core connection resiliency in Azure SQL: EnableRetryOnFailure, handling transactions with CreateExecutionStrategy, and idempotency.
Updated Published 6 min read
Dapper vs EF Core in ASP.NET Core: performance realities, change tracking overhead, sharing transactions in hybrid architectures, and when to use each tool.
Updated Published 5 min read
PAGELATCH_UP on database id 2 is TempDB allocation contention. Multiple equal data files fix it. EF does not need a #temp table to cause this.
Updated Published 5 min read
Readers blocking writers deadlock an ASP.NET Core API. READ_COMMITTED_SNAPSHOT (RCSI) uses row versions. It does not fix lost updates on the same row.
Updated Published 5 min read
EF Core value conversions for enums and value objects: HasConversion maps EmailAddress or enum to one SQL column — and what LINQ cannot query inside a JSON blob.
5 min read
SQL Server parameter sniffing in EF Core: same LINQ fast for one clinic, 30s timeout for another. How to confirm in Query Store and what to change.
4 min read
EF Core optimistic concurrency with RowVersion: map SQL Server rowversion as a concurrency token, return 409 on conflict, and stop last-write-wins on encounters.
Updated Published 5 min read
EF Core SaveChanges interceptors for audit logs: ISaveChangesInterceptor stamps CreatedBy and ModifiedBy from the JWT. Why ExecuteUpdate bypasses it.
4 min read
EF Core global query filters for soft delete and multi-tenant ClinicId. How HasQueryFilter works — and why IgnoreQueryFilters is how tenant data leaks.
4 min read
EF Core ExecuteUpdateAsync runs one set-based UPDATE without loading entities. When to use it for bulk fee schedules — and why it skips SaveChanges interceptors.
Updated Published 5 min read
AsNoTracking vs identity resolution in EF Core: when no-tracking duplicates Patient objects on every row, and when AsNoTrackingWithIdentityResolution fixes read-only Includes.