iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Improve Entity Framework Core (EF Core) performance by finding the actual bottleneck first, then reducing unnecessary database work, roundtrips, transferred data, and object tracking. Start with command timings and the database execution plan; only consider compiled queries, context pooling, or other EF runtime optimizations after measuring a representative workload.
1. Find the slow layer before changing code
A slow operation that uses EF Core is not automatically an EF Core problem. Database execution, network latency, repeated commands, application-side processing, and EF overhead can all contribute. Diagnose a repeatable slow request or operation before choosing an optimization.
- Capture EF Core command logs briefly. Look for slow SQL statements, unexpected repeated commands, and the time taken by each command. Logging adds overhead and can consume disk space, so use it for a short diagnostic interval or in preproduction rather than leaving verbose logging enabled indiscriminately.
- Connect SQL to its LINQ call site. Query tags can help identify which part of the application generated a logged query.
- Inspect the database execution plan. Check whether the query uses appropriate indexes and whether its access path fits the data. A plan can change with data size and distribution, so a tiny development database may give a misleading picture.
- Check EF-specific behavior. EF metrics can help expose issues such as query-cache behavior or undisposed contexts.
- Benchmark plausible fixes. Use data that resembles production. Microsoft recommends BenchmarkDotNet for controlled benchmarking, but single-thread measurements do not replace tests under concurrent load.
Microsoft’s EF Core performance guidance advises developers to diagnose carefully rather than assume where the root cause lies. Its 2022 diagnosis sample illustrates the possible impact of reducing materialization and doing work in the database: for averaging blog rankings, the reported times were 2,860.4 μs for loading tracked entities, 1,353.0 μs for no-tracking entities, 910.9 μs for projecting only the ranking, and 627.1 μs for calculating the average in the database. These are results from that sample setup, not predictions for another application.
2. Improve the database query path
Verify index use in the execution plan
LINQ syntax alone does not tell you whether a database can use an index efficiently. Inspect the plan for the actual query and your data. In Microsoft’s SQL Server example, a filter using StartsWith can use an index where a similar EndsWith filter cannot. The point is not that one method is always faster, but that seemingly similar predicates can lead to different access paths.
#1 Best Overall
Index design involves tradeoffs. Indexes can speed selected reads, but they add work when data is updated. For a composite index on (A, B), column order matters: it can support filters on both columns and often on A alone, but not a filter on B alone. Expressions over columns may also prevent use of a simple index; depending on the database provider, a persisted computed column or an expression index may be an option. Verify provider support and confirm the result in the plan rather than adding indexes speculatively.
Select only the values the caller needs
If a screen, API response, or calculation needs only a few fields, project those fields with Select instead of materializing complete entities and transferring unused columns. For several values, project to a DTO or anonymous type. This is particularly straightforward for read-only work; EF Core change tracking operates on entity instances, so use entity results when the operation needs tracked entities for later changes.
Put a deliberate bound on result sets
Large, unbounded results increase database work, network transfer, memory use, and downstream processing. Set a result limit when the application only needs a bounded number of rows, and paginate when users need to navigate a larger result set.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Skip/Take pagination, commonly translated to SQL OFFSET/LIMIT, is intuitive but can become inefficient on deep pages. For sequential navigation, keyset pagination is often a better fit. The right choice depends on how users move through results and on provider behavior.
Choose relationship loading to control roundtrips and duplication
When related data is known to be needed, eager loading can avoid the repeated roundtrips associated with lazy loading. But loading several related collections in one query can duplicate parent data through join expansion, sometimes called cartesian explosion. Split queries can reduce that duplication, at the cost of potentially adding roundtrips. Compare the generated SQL, transferred rows, and command count for the actual shape of the result.
Use tracking only when the operation needs it
For read-only entity queries, AsNoTracking avoids change-tracking work. Keep tracking when the application will modify the returned entities and rely on change detection. If a no-tracking result contains repeated references to the same entity and identity resolution matters, no-tracking with identity resolution is a possible middle ground. These are workload choices, not universal defaults.
Manage memory, asynchronous I/O, and raw SQL deliberately
- Buffering: Methods such as
ToListAsyncretain the result set in memory. This can be appropriate when the full result is needed, but large results consume more memory. - Streaming: Async enumeration can keep memory use bounded while processing a large result set. The application still has to consume and process all requested rows.
- Async database calls: Use async database APIs in scalable applications so threads are not blocked during I/O, and avoid accidental mixing of synchronous and asynchronous paths. Microsoft notes known async issues in some
Microsoft.Data.SqlClientscenarios, especially with large text or binary values; investigate unexpected behavior against the exact driver and version. - Raw SQL: Consider it when EF Core cannot express or translate a needed database-specific construct and the performance gain justifies the added maintenance cost. First inspect the SQL EF Core already generates; raw SQL is a last resort, not a default optimization.
3. Make updates with fewer unnecessary operations
Understand SaveChanges batching for your provider
EF Core can batch multiple statements from SaveChanges into fewer roundtrips, but batching behavior depends on the database provider. Microsoft’s SQL Server guidance says batching tends to be less efficient below four statements, with benefits diminishing after about 40; the cited SQL Server default maximum batch size is 42. Those figures are provider-specific guidance, not universal thresholds or a reason to change settings without a benchmark.
Use set-based operations for uniform changes
ExecuteUpdateAsync and ExecuteDeleteAsync, available starting with EF Core 7.0, can update or delete many rows in a single SQL statement without loading every entity or running change tracking for the operation. They suit uniform set-based changes; they are not interchangeable with every workflow that edits individual tracked entities.
Because these methods change the execution model, account for transaction boundaries and concurrency expectations. Any matching entities already tracked by the current context may be stale after the set-based operation; do not assume EF refreshed their in-memory values.
4. Reduce EF Core runtime overhead only after query work
Microsoft’s overall optimization order is useful: query efficiency, indexes, and fewer roundtrips usually matter more than EF Core’s own runtime overhead, while database I/O and network latency often dominate. If measurement shows EF overhead is material in a hot path, consider these options.
Keep query shapes reusable; benchmark compiled queries
EF Core caches query compilation by expression-tree shape. Parameterize changing values so structurally identical queries can reuse compiled results. Dynamically embedding changing constants in expression trees can create cache misses and distinct SQL.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Compiled queries bypass cache lookup for selected hot query shapes. They require a single EF model and simple scalar parameters, and their benefit should be measured. Microsoft’s sample benchmark reports the following compiled versus non-compiled times:
Rank #4
| Sample query size | Compiled | Non-compiled |
|---|---|---|
| One blog | 564.2 μs | 671.6 μs |
| Ten blogs | 645.3 μs | 709.8 μs |
These are Microsoft sample measurements, not expected gains for an application with different queries, data, or infrastructure.
Consider DbContext pooling for measured setup costs
DbContext pooling reuses initialized context instances and may reduce setup overhead in high-performance, low-latency workloads. It is separate from database connection pooling. In Microsoft’s single-threaded benchmark fetching one row from a local SQL Server database, the reported result was 701.6 μs and 50.38 KB allocated without context pooling, compared with 350.1 μs and 4.63 KB with pooling. The source cautions that results vary with row count, network latency, and contention; treat this as a sample, not a forecast.
A pooled context is reused across scopes, and OnConfiguring runs only when the context is first created. Do not put per-request or tenant-varying state there. Pool sizing and state reset need care so one use of a context cannot leak state into another.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsDo not disable safety checks casually
Disabling EF Core thread-safety checks is another possible runtime-overhead reduction, but it can hide concurrent use of a DbContext, which is unsupported. Consider it only after thorough testing for concurrency bugs.
Best Value
5. Weigh model changes against consistency and maintenance
Denormalization and cached values
Denormalization or cached aggregate values can reduce joins or repeated calculations, but they add synchronization and consistency work. A stored computed column is suitable for a value derived from columns in the same row. A cached value that depends on other rows needs a reliable update mechanism. Database triggers can update values in the transaction and avoid extra application roundtrips, though EF Core has no dedicated trigger-authoring API. Materialized or indexed views can cache query results, but refresh and update behavior varies by database.
Inheritance mapping
Inheritance mapping affects query shape: table-per-hierarchy (TPH) stores a hierarchy in one table; table-per-type (TPT) splits types across tables and may require joins; table-per-concrete-type (TPC) uses tables for concrete types. Microsoft’s 2023 sample loaded all rows from a seven-type hierarchy with 5,000 rows per type, or 35,000 total:
| Mapping strategy | Microsoft sample time |
|---|---|
| TPH | 149.0 ms |
| TPT | 312.9 ms |
| TPC | 158.2 ms |
The example demonstrates a tradeoff in that particular all-rows query; actual results depend on the query and the number of hierarchy tables. It is not a blanket performance verdict for every model.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall6. Use a measured optimization loop
- Reproduce a slow operation with representative data and capture command timings.
- Inspect generated SQL and the database execution plan; identify expensive access paths, missing or ineffective indexes, repeated roundtrips, and oversized results.
- Change one relevant factor at a time: projection, result bounds, relationship loading, tracking, pagination, or update strategy.
- Benchmark the changed version under realistic conditions, then test concurrent load when production concurrency matters.
- Only if the remaining measured cost is EF runtime overhead, test query-shape reuse, compiled queries, or context pooling.
Confirm API availability, provider behavior, and provider-specific defaults for the EF Core and database versions you deploy. The Microsoft figures here are sample benchmark results published in 2022 and 2023, not universal gains.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

