
You may encounter performance issues when using Entity Framework as your object-relational mapper. These performance issues can cause your apps to run slowly and become less effective. Common causes include incorrect settings, slow bulk operations, and excessive memory usage when tracking many entities. Default change tracking can quickly consume resources. Utilizing methods like AsNoTracking can help your app perform better for read-heavy queries. If you use EF to enforce domain rules and leverage the Fluent API, you can prevent some performance issues. By learning key performance tips, you can avoid these issues and optimize EF Core’s efficiency. Monitor how your app interacts with the database and identify performance issues early before they impact your users.
Your app might run slower than you want. This can mean there are problems with your entity framework core setup. Watch for these signs and use good habits to fix them:
| Common Symptoms | Best Practices |
|---|---|
| Slow Where or Join queries | Add indexes on columns you filter often |
| High database CPU usage | Check the SQL made by logging |
If you notice slow queries or high CPU use, look at how your queries work with the database. Sometimes, missing indexes or bad queries cause these issues. You can make things faster by checking your queries and adding indexes where needed.
You need helpful tools to find and fix slow spots in entity framework core. These tools show what happens in the background and help you see how your app performs.
These tools let you check how long queries take, find extra database calls, and spot bad SQL from LINQ. For example, entity framework core lets you see command times with easy logging. If you set logging to Information, you can see each command and how long it takes. This helps you find slow parts fast.
You can also use tools like Glimpse and Performance Counters for more details. Using these tools gives you a clear view of performance and helps you make smart choices to improve your ef apps.

The n+1 problem happens when you load related data in a way that causes many extra queries. In entity framework core, this often appears when you access related data inside a loop. For example, you might fetch 100 orders. Then, for each order, you access the customer property. This triggers one query for all orders and then 100 more queries for each customer. You end up with 101 queries instead of just one. This slows down your app, especially when you have lots of data. You may not notice this issue with small datasets, but it becomes a big problem in production.
You can spot the n+1 problem by watching how many queries your app sends to the database. Here are some ways to detect it:
Tip: Always check your logs and profiling tools after you add new queries or change how you load related data.
You can fix the n+1 problem in entity framework core by using eager loading. Eager loading lets you fetch all the data you need in one query. You do this by using the Include method. This tells ef to join related tables and get everything at once. For example, if you want to get authors and their books, you can write:
var authorsWithBooks = context.Authors.Include(a => a.Books).ToList();
This code runs a single query that joins authors and books. Your app gets all the data in one go. Eager loading reduces the number of queries and makes your app faster. You should use eager loading when you know you need related data right away. This helps you avoid slowdowns and keeps your app running smoothly.
Sometimes, ef core queries can run slowly. There are many reasons for this. Check the table below to see the main causes:
| Cause | Explanation |
|---|---|
| Indexing issues | Queries that do not use indexes can slow down your database, especially with large data sets. |
| Lazy loading | This feature loads related data only when you access it. It can create many extra database queries and hurt performance. |
| Buffering vs. streaming | Buffering loads all query results into memory. Streaming processes one result at a time, which uses less memory and works better for large data sets. |
| Tracking and identity resolution | Entity framework tracks changes by default. This can slow down performance when you load many entities. |
If your queries are slow, look for these problems.
You can make ef core queries better by checking the sql they create. When you write linq queries, entity framework core turns them into sql. Sometimes, the sql is too big or has extra joins. You should look at the raw sql to see if it is what you want. Use logging tools to see the sql and find problems. For example, using multi-level Include can make big results and slow things down. You can use split queries to break up hard queries and make them faster. Always check the sql to find slow spots and make things better.
Here are some ways to make your ef core queries faster:
It is important to know how ef core queries work. This helps you write good queries and get the best speed from your database. Always test your queries with real data and watch how much memory and time they use. Streaming results can also help when you have lots of data.
Entity framework core keeps track of every entity you load. This is the default setting. Tracking helps ef know if you change something. But tracking can slow your app down. It is slower with big data sets. You may see your app use more memory. Queries can also take longer when tracking is on. The table below shows how tracking changes performance:
| Method | Mean | Allocated |
|---|---|---|
| Query_Tracked | 45.3 ms | 12.1 MB |
| Query_AsNoTracking | 28.7 ms | 6.3 MB |
Using asnotracking makes queries faster. It also uses less memory. Your app can answer faster and help more users at once.
Tip: If you turn off tracking, your queries run faster. This is best when you only need to read data.
Use asnotracking if you do not want to update or delete the data. Most web APIs just read data. About 70-80% of endpoints only show information. These work better with asnotracking. Here are some good times to use it:
Turning off tracking in these cases makes your app faster. Asnotracking is important for read-only situations. It stops extra work from tracking every entity.
Entity framework core tracks changes so you can update data. For normal use, keep AutoDetectChangesEnabled set to true. If you add or update many records at once, turn off AutoDetectChanges. This makes things faster. For example, adding thousands of rows is much quicker this way. It can go from over 40 minutes to under 3 minutes. You can use tools like EFCore.BulkExtensions to make bulk work even faster. Always turn AutoDetectChanges back on after you finish bulk work.
Remember to use asnotracking for read-only queries. Use tracking when you want to update data. This gives you the best speed and keeps your data safe.

There are different ways to get related data in entity framework core. The two main ways are eager loading and lazy loading. Eager loading uses the .Include() method. It gets all the related data in one query. Lazy loading waits until you use the related data. Then it runs a new query each time you access it. This can make lots of extra queries and slow things down.
Here is a table that shows the main differences:
| Aspect | Eager Loading | Lazy Loading |
|---|---|---|
| Timing of Data Retrieval | Fetches all related data in a single query upfront. | Defers loading of related entities until accessed. |
| Efficiency and Performance | More efficient for multiple entities, reducing hits. | May incur additional queries, affecting performance. |
| Usage and Control | Provides control over initial data retrieval. | Offers convenience but can lead to performance issues. |
Eager loading is good when you need related data right away. Lazy loading gives you more choices, but it can cause the N+1 problem. Your app might run many queries if you loop through related items.
Tip: Pick eager loading if you want to stop extra database calls and make your queries faster.
You need to choose the best way to load data for your app. Eager loading is best when you always need the related data. Lazy loading is helpful if you want your first query to be small. But you should watch out for hidden slowdowns. Explicit loading lets you decide when to get related data. This is good for big data sets or when you want more control.
Here is a table to help you decide:
| Loading Strategy | Description | Pros | Cons |
|---|---|---|---|
| Eager Loading | Load related data up front using .Include(). | Great when you know you’ll always need the related data. | Can over-fetch if you don’t need everything. |
| Lazy Loading | Related data is fetched on-demand (when first accessed). | Reduces initial query size. | Multiple queries → risk of N+1 problems. |
| Explicit Loading | You manually control when to load related entities. | Perfect for fine-grained scenarios where you sometimes need related data. | Adds extra code but gives maximum control. |
You can make entity framework faster by picking the right way to load data for each case. Always test your queries and see how your app works.
When you use ef with lots of data, you can have big problems. Loading too much at once can make your app slow or even crash. Here are some things that can happen:
The N+1 selects problem has been a problem for databases for a long time. It means your app sends many small queries instead of one big one. This can make things much slower.
If you do not use the right way to get data, your app might freeze. You should not load all the records at the same time. This helps your app stay healthy and keeps users happy.
You can fix these problems by using pagination. Pagination splits your data into smaller pieces. This makes your app faster and easier to use. In ef, you can use Skip() and Take() to do this. Skip lets you move past some records. Take gets only the number you want. This helps you work with big data and keeps your app running well.
If you use ToList() to get everything, your app can slow down or stop. Instead, just get the data you need for each page. This keeps memory use low and makes your app answer fast.
Offset pagination is simple with Skip and Take. But for really big tables, keyset pagination can be better.
Keyset pagination is a better way for big data. It does not skip rows. It starts after the last record you got. This makes it faster and more steady.
Pick the best way to use pagination for your app. This will help your app stay quick and work well, even with lots of data.
You can make your app faster by using DTO projection. DTO means Data Transfer Object. With DTOs, you only pick the fields you need from the database. This makes your queries quicker and saves memory. Here are some good things about using DTOs:
DTO projection is great when you want to show lists or details but not every property. This keeps your app light and quick.
Compiled queries help your app run faster if you use the same query a lot. EF lets you compile queries so they work quicker. Use compiled queries where speed is very important. Here is a table that shows the good and bad sides:
| Benefits of Compiled Queries | Limitations of Compiled Queries |
|---|---|
| Performance improvements when executing the same query multiple times | Can complicate debugging and testing due to runtime SQL generation |
| Caching mechanisms that enhance efficiency | Increased complexity in code, making it harder to understand |
Use compiled queries for busy APIs, background jobs, and places where the same query runs many times each second. Compiled queries can make things 8 to 12 percent faster, but they are best for special cases. Do not use them for rare or changing queries.
Async operations help your app handle more users and stay fast. When you use async methods, your app does not stop and wait for the database. This means your app can do other things at the same time. It is important for busy websites. Async APIs keep your app quick even when lots of people use it. You should use async/await in your queries to help your app run well.
Caching makes your app faster by saving data or query plans. When you use ef, there are different kinds of caching. Each kind helps in its own way and works best in certain cases.
| Caching Type | Description | Performance Impact |
|---|---|---|
| Query Plan Caching | Saves how queries run, so similar queries can use the same plan. | Makes repeated queries faster by skipping new plans. |
| Results Caching | Keeps query answers in a local spot, so you do not ask the database again. | Makes things much quicker by lowering database work and wait time. |
| Autocompiled Queries | Saves LINQ queries so you can use them again. | Makes things faster by not making new plans for the same queries. |
You can use results caching, also called second-level caching, with tools like NCache. Entity Framework 5 can save LINQ queries for you with autocompiled queries. If you change how you write a query, ef might make a new plan and not use the saved one.
Tip: Caching is best when you run the same queries a lot or your data does not change often.
Use caching for data you look up many times or that takes a long time to get. Data that does not change much, like country or currency lists, is great for caching. Big API answers also work well with caching.
If you cache data that changes a lot, you might show old info. Make sure you update or clear the cache when the data changes. Caching makes your app answer faster and helps your database do less work.
You can make your database work faster with bulk operations. These methods let you change many records at the same time. Standard EF methods are fine for small jobs. But they get slow when you have lots of data. Bulk operations help you insert, update, or delete thousands of rows quickly. You might use them when you import data from CSV files. They also help when you sync records or do nightly updates.
Here is a table that shows how much faster bulk operations are than standard EF methods:
| Operation Type | EF Core Execution Time | EF Extensions Execution Time | Performance Improvement |
|---|---|---|---|
| Bulk Insert | ~2,800 ms | ~177 ms | ~15x faster |
| Bulk Update | ~1,500 ms | ~100 ms | ~15x faster |
| Bulk Delete | ~800 ms | ~31 ms | over 25x faster |
| Bulk SaveChanges | ~1,400 ms | ~425 ms | ~3.3x faster |

You can see that bulk operations are much faster. They are great for big jobs like moving lots of data or updating inventory.
Batching lets you group actions so your app works faster. You can use batch updates with UpdateRange() to change many things at once. Try not to call SaveChanges() too often. Each call sends data to the database. EF Core 7 has new methods like ExecuteUpdate and ExecuteDelete for bulk changes.
Here are some ways to batch your work:
UpdateRange() for batch updates.SaveChanges() less to cut down on database trips.ExecuteUpdate and ExecuteDelete in EF Core 7 for faster bulk changes.You can use code like this to batch updates:
public void BatchUpdateAuthors(List authors)
{
var students = this.Authors.Where(a => a.Id > 10).ToList();
this.UpdateRange(authors);
SaveChanges();
}
For EF Core 7 or later, you can use:
_context.Authors.Where(a => a.Id > 10).ExecuteUpdate();
Batching and bulk operations help your database work better. You can finish big jobs fast and keep your app running well.
Indexes help your database find data faster. When you search for something, the database looks at the index first. This makes your app answer more quickly. But if you add or change records a lot, indexes can slow things down. The database has to keep the index updated every time you change data.
You need to have the right number of indexes. Too many indexes can make updates slow. Not enough indexes can make searches slow. Always check your queries and change indexes to get the best speed.
When you change your database with migrations, you should watch your indexes. Add indexes to columns you search a lot. This helps your database work faster and keeps your app smooth. You can set up indexes in your ef model so they are made in the database for you.
You can add an index in your ef model like this:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasIndex(b => b.Title) .HasDatabaseName("Index_Title"); }
Check your database every time you do a migration. Make sure you add indexes to columns people search or filter. Take away indexes you do not need. This keeps your database neat and fast. You can use tools to find slow queries and add indexes where they help most.
If you plan your indexes well, your database will do searches and updates better. Your ef apps will run faster and work better.
You must watch how long you use DbContext. If you use it too long, your app can slow down. It is best to make a new DbContext for each job. For example, make one for each web request or background task. This stops memory leaks and keeps your data up to date.
Here is a table with tips for using DbContext:
| Best Practice | Description |
|---|---|
| Avoid DbContext pooling | Pooling DbContext can cause old data or problems with many users. Use DbContext for short jobs. |
| Use AsNoTracking | Use AsNoTracking when you only need to read data. This makes things faster because it skips tracking. |
| Avoid lazy loading | Lazy loading can make lots of database calls. This can slow your app down. |
Use AsNoTracking if you just want to look at data. This makes your queries run faster and saves memory. Try not to use lazy loading. It can make your app send extra calls to the database and slow things down.
Tip: Always get rid of your DbContext when you are done. This keeps your app working well and stops memory leaks.
You should open a database connection only when you need it. Close it right after you finish. This helps your database not run out of connections.
DbContext pooling can help if your app is very busy. Pooling saves time by reusing DbContext objects. This can make your app faster, but you must be careful. Do not use the same DbContext in more than one thread.
| Best Practice | Description |
|---|---|
| DbContext pooling | Pooling helps busy apps by saving time. Use it carefully so you do not have problems. |
Never keep a DbContext for too long. Use it for one job, then let it go. This keeps your app quick and your data safe. Good connection habits help your ef app work well and handle more users.
You can make ef faster by using smart methods. Use Include or projection so you do not get N+1 queries. Add AsNoTracking when you only read data. Pick just the columns you need. Make sure your filters turn into SQL. Group your changes and save them all at once. Put indexes on columns you filter for quicker searches. Use IDbContextFactory for background jobs. Compiled queries are good for requests that happen a lot.
Check your app often with tools like Visual Studio Profiler or dotTrace. Look at your database and how you write queries. This helps your app stay quick and work well.
Focus: entity framework performance issues and practical steps to speed up EF Core queries.
You see the N+1 problem when your app sends many small queries instead of one big query. This happens when you load related data inside a loop. Use eager loading to fix it.
You can use AsNoTracking() for read-only queries. This skips change tracking and makes your queries faster. Your app uses less memory and responds quicker.
Bulk operations work best when you need to insert, update, or delete thousands of records. You save time and reduce database load. Use tools like EFCore.BulkExtensions for big jobs.
Indexes help your database find data faster. You should add indexes to columns you filter or search often. This makes your queries run quickly and keeps your app responsive.
You can use EF Core logging, MiniProfiler, and SQL Server Profiler. These tools show slow queries and help you spot bottlenecks. Check logs often to keep your app running well.
EF Core can be slower because an ORM generates SQL queries, tracks net objects and materializes results which adds overhead compared to hand-written sql query or ADO.NET; EF Core may also produce different queries than you expect, or include unnecessary joins and selects. To improve performance, inspect the actual query EF uses, profile the database server, cache queries where appropriate, and consider using compiled queries or write a custom SQL or use dapper for critical paths.
Large amount of data amplifies ORM overhead: tracking many entities consumes memory and CPU, and sending big result sets across the network stresses database access. Use pagination, projection to DTOs, AsNoTracking for read-only workloads, and avoid loading entire collections. If you need to insert a lot of data, use bulk operations or ADO.NET batching instead of inserting one entity at a time through an EF context.
EF Core is optimized for the net core and offers modern improvements, but ef 6 has mature behaviors and optimizations for some scenarios. Benchmark your workload: ef core queries are slow in some complex queries but faster in others. Consider the net ecosystem, your existing codebase, and whether you can use dapper or ado.net for hotspots. Often using EF Core with tuned queries and compiled queries yields the best balance.
Creating and disposing contexts too frequently or keeping a long-lived context that tracks many entities causes performance and memory issues. Opening and closing connections is handled by the context, but repeatedly creating contexts can add overhead. Use context per unit of work, avoid long tracking lifetimes, and when reading, use AsNoTracking to reduce the performance penalty of change tracking.
Look at an example by logging the actual query and parameters EF uses, enable database server profiling, and compare execution plans to your hand-written sql query. Use tools in asp.net core or EF logging to capture queries, and check whether EF caches queries or recompiles LINQ expressions; using compiled queries can avoid the first compile cost for repeated queries.
Use dapper for simple high-performance data access scenarios where you want minimal ORM overhead and direct control of SQL. Dapper maps results to objects quickly and has less tracking overhead than ORMs. For complex queries, or when you need full ORM features like change tracking, EF is useful. Mixing EF and dapper is common: EF for domain modeling and dapper for performance-critical read paths.
Complex queries may produce inefficient SQL or multiple round-trips; EF may translate LINQ into several different queries or use subqueries that stress the database. Simplify LINQ, project only needed fields, or write raw SQL when necessary. Consider splitting queries, adding proper indexes on the database server, and measuring execution plans to improve performance.
Yes, using an orm introduces overhead for change tracking, query generation, and materialization compared to manual ADO.NET. The penalty varies by scenario: small CRUD apps see negligible impact, while high-throughput systems may suffer. To mitigate, reduce tracking, use projections, avoid unnecessary queries, or use ADO.NET/dapper for tight loops and bulk operations.
For bulk inserts, disable change tracking temporarily, batch saves (SaveChanges in groups), or use a bulk insert library that uses native database bulk APIs. Avoid inserting one entity and SaveChanges per row. If you must insert a lot of data regularly, consider using ADO.NET or write a custom bulk loader for the database which bypasses EF overhead.
EF Core compiles LINQ to an internal representation and can cache some query plans, but the first compile of a query has cost; repeated similar queries benefit from caching or using compiled queries explicitly. Caching the results at the application layer can also reduce database load. Be careful to cache based on parameters and invalidate when underlying data changes to avoid stale results.
The database server is often the bottleneck: slow disk, missing indexes, poor query plans, or resource contention will make ef core queries are slow regardless of ORM choice. Optimize indexes, update statistics, and tune queries at the server. Use execution plans to identify slow operations and adjust either the EF query or the database schema accordingly.
Use EF logging, query tags, and performance profilers to capture the actual query and execution time. Tools like SQL Server Profiler, Query Store, or third-party profilers can show the database perspective. Measure end-to-end latency in the asp.net core app, identify hotspots, and test different approaches (AsNoTracking, compiled queries, dapper) to see measurable improvements.
For complex reporting where the ORM produces inefficient SQL or you need specific optimizations, write a custom sql query or use dapper to execute the query and map results. Reporting often benefits from tuned, hand-crafted queries that the database server can optimize better than the generic SQL generated by EF.
Think EF in terms of unit of work: creating and disposing contexts per request or per transaction helps prevent accumulating tracked net objects. Avoid attaching large graphs unnecessarily, detach when appropriate, and monitor memory usage. Properly disposing the context and minimizing tracked entities reduces GC pressure and improves performance.
🚀 Want to be part of m365.fm?
Then stop just listening… and start showing up.
👉 Connect with me on LinkedIn and let’s make something happen:
This isn’t just a podcast — it’s a platform for people who take action.
🔥 Most people wait. The best ones don’t.
👉 Connect with me on LinkedIn and send me a message:
“I want in”
Let’s build something awesome 👊