7 Common EF Core Mistakes That Hurt Performance
Most EF Core performance problems are not caused by SQL Server.
7 Common EF Core Mistakes That Hurt Performance

Most EF Core performance problems are not caused by SQL Server.
They are caused by seemingly harmless LINQ queries.
A ToList() in the wrong place.
An unnecessary Include().
Loading entire entities when only three columns are needed.
Everything works during development.
Then production data arrives.
Queries that once took milliseconds suddenly take seconds. Memory usage grows. API response times become unpredictable.
The good news is that most of these issues are avoidable.
Let’s look at the mistakes that silently make EF Core applications slower over time.
1. Calling ToList() Too Early
Most developers know that LINQ queries are executed lazily.
Yet one of the most common EF Core performance mistakes is forcing query execution too early.
When ToList() is called before filtering, sorting, or projection operations are completed, EF Core loads more data than necessary into memory and shifts the workload from the database to the application.
In development environments with a small dataset, this often goes unnoticed.
In production, however, the same query can consume significant memory and dramatically increase response times.
Bad:
var products = await context.Products
.ToListAsync();
var expensiveProducts = products
.Where(p => p.Price > 100)
.ToList();
Better:
var expensiveProducts = await context.Products
.Where(p => p.Price > 100)
.ToListAsync();
The first query loads the entire table.
The second query allows the database engine to perform the filtering efficiently.
2. The Hidden Cost of Include()
Include() is one of the most useful features in EF Core.
It is also one of the most abused.
Many developers add multiple Includes as a safety net, assuming that loading extra data is harmless.
Unfortunately, every Include increases query complexity, data transfer size, and memory usage.
As the number of relationships grows, generated SQL can become surprisingly expensive.
However Include()is not inherently bad. The problem starts when related data is loaded but never used.
Example:
var orders = await context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.Include(o => o.Payments)
.ToListAsync();
At first glance, this looks convenient.
However, the application may now be loading thousands of records that are never actually used.
If the endpoint only needs a small subset of information, projection is often a much better choice.
3. Loading Entire Entities When You Only Need a Few Columns
One of the easiest ways to waste resources is loading complete entities when only a few fields are required.
Developers often think in terms of objects.
Databases think in terms of data.
If an API endpoint only needs a product name and price, loading twenty additional columns provides no value.
Less Efficient:
var products = await context.Products
.ToListAsync();
Better:
var products = await context.Products
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price
})
.ToListAsync();
Projection reduces network traffic, memory consumption, and materialization costs.
In high-traffic systems, these savings add up quickly.
4. Tracking Data That Will Never Be Updated
By default, EF Core tracks every entity it loads.
This behavior is useful when changes need to be saved back to the database.
For read-only queries, however, tracking introduces unnecessary overhead.
The Change Tracker consumes both memory and CPU resources while monitoring objects that may never be modified.
Default Tracking:
var products = await context.Products
.ToListAsync();
Read-Only Query:
var products = await context.Products
.AsNoTracking()
.ToListAsync();
For reporting endpoints, dashboards, and public APIs, AsNoTracking() can significantly improve performance.
A surprising number of applications leave tracking enabled everywhere without realizing the cost.
5. Ignoring Cartesian Explosion and Split Queries
As applications evolve, queries often require multiple collection Includes.
This can lead to a problem known as Cartesian Explosion.
The database returns duplicate rows because every combination of related records must be represented in the result set.
Example:
var blogs = await context.Blogs
.Include(b => b.Posts)
.Include(b => b.Tags)
.ToListAsync();
What appears to be a simple query may generate a massive result set internally.
EF Core provides a solution through Split Queries.
var blogs = await context.Blogs
.Include(b => b.Posts)
.Include(b => b.Tags)
.AsSplitQuery()
.ToListAsync();
Instead of producing one large query, EF Core generates multiple smaller queries that are often easier for both the database and application to process.
6. Forgetting About Compiled Queries
Every time EF Core executes a LINQ query, it must translate that query into SQL.
For most applications, this overhead is negligible. In fact, EF Core already performs query caching internally, so Compiled Queries are usually unnecessary for everyday workloads.
However, for hot paths executed thousands of times per minute, the translation overhead can become measurable. In those scenarios, Compiled Queries may still provide a small but meaningful performance improvement.
Compiled Queries allow EF Core to cache the translation process and reuse it across executions.
Example:
private static readonly Func<AppDbContext, int, Product?>
GetProductById =
EF.CompileQuery(
(AppDbContext context, int id) =>
context.Products
.FirstOrDefault(p => p.Id == id)
);
Usage:
var product = GetProductById(context, id);
Compiled Queries should not be used everywhere.
In many applications, proper indexing, efficient projections, and reducing unnecessary Includes will have a much larger impact on performance.
However, for frequently executed queries in performance-critical applications, Compiled Queries can still provide measurable improvements.
7. Blaming EF Core Instead of Missing Indexes
Many developers spend hours optimizing LINQ expressions while ignoring the database itself.
A perfectly written EF Core query can still perform poorly if the database lacks appropriate indexes.
Example:
var customer = await context.Customers
.FirstOrDefaultAsync(c => c.Email == email);
The query looks harmless.
Without an index on Email, the database may scan every row in the table.
Index Configuration:
modelBuilder.Entity<Customer>()
.HasIndex(c => c.Email);
Indexes often have a greater impact on performance than any ORM optimization.
Before optimizing EF Core, always verify how the database executes the generated SQL.
Conclusion
Most EF Core performance problems are not caused by the framework itself.
They are caused by how we shape our queries.
Too many Includes. Too much data loaded by default. Too little attention to what actually runs in SQL.
EF Core does exactly what you ask it to do.
The real question is whether you are asking for too much data without realizing it.
In most cases, the biggest performance improvement is not writing more complex queries — it is simply loading less.
메타데이터
- post_id
- 5bb62349b00a
- slug
- 7-common-ef-core-mistakes-that-hurt-performance-5bb62349b00a
- url
- https://blog.devgenius.io/7-common-ef-core-mistakes-that-hurt-performance-5bb62349b00a
- canonical_url
- https://blog.devgenius.io/7-common-ef-core-mistakes-that-hurt-performance-5bb62349b00a
- author_url
- https://medium.com/@azizkale
- status
- ok
- fetched_at
- 2026-06-22 17:31:34