Hjelle.Dev

Entity framework gotcha you didn’t know about

I was recently reviewing some underperforming code. It was a very large and complex piece of code. It included running several queries in parallel using SemaphoreSlim. If you want to know more about why and how you can do that, check my article about it.

While reviewing the performance data, I noticed a recurring call taking far longer than it should have. Some of them where about 20 seconds, they should have been mere milliseconds. It was a query to select FirstOrDefault(x => x.Id == doc.Id). It did include several .Include, .ThenInclude and .AsSingleQuery(), but nothing too bad.

Get NordVPN - the best VPN service for a safer and faster internet experience. Get up to 77% off + 3 extra months with a 2-year plan! Take your online security to the next level

Get NordVPN - Secure Your Connection

This is an affiliate link. I may earn a commission when you click on it and make a purchase at no extra cost to you.

If you look at the example below I have a queryable extension method named LoadRelatedData(). This is just a bunch of Includes and ThenIncludes.

var docInNewContext = await db.Documents
   .LoadRelatedData()
   .Include(d => d.Copies)
   .AsSingleQuery()
   .FirstOrDefaultAsync(d => d.Id == doc.Id);

I always thought that .net and Entity Framework worked miracles and created the perfect SQL code behind the scenes. How wrong was I?

Get NordVPN - the best VPN service for a safer and faster internet experience. Get up to 77% off + 3 extra months with a 2-year plan! Take your online security to the next level

Get NordVPN - Secure Your Connection

This is an affiliate link. I may earn a commission when you click on it and make a purchase at no extra cost to you.

Entity Framework does create a good translation of the query, but it can sometimes need a little bit of help. I discovered that by introducing a .Where(d => d.Id == doc.Id) right after the db.Documents entity I got a better and tighter query plan. View the changes I made below:

 var docInNewContext = await db.Documents
   .Where(d => d.Id == doc.Id)
   .LoadRelatedData()
   .Include(d => d.Copies)
   .AsSingleQuery()
   .FirstOrDefaultAsync(); 

After moving the logic for finding data for a specific document my query executed in mere milliseconds. So while EF Core is smart enough to optimize this into a single efficient SQL query, putting the filter earlier can help EF generate a tighter query plan, especially if the data set is large. Hope this will save someone else from making my mistake.

Get NordVPN - the best VPN service for a safer and faster internet experience. Get up to 77% off + 3 extra months with a 2-year plan! Take your online security to the next level

Get NordVPN - Secure Your Connection

This is an affiliate link. I may earn a commission when you click on it and make a purchase at no extra cost to you.

5/9/2025
Related Posts
Performance boosting with SemaphoreSlim

Performance boosting with SemaphoreSlim

Effective performance tuning starts by using analytics and profiling tools such as Azure Application Insights or dotTrace to identify your slowest endpoints, then introducing controlled parallelism with SemaphoreSlim to accelerate CPU- or I/O-bound work. By carefully adjusting the number of concurrent operations and validating under realistic, production-like load—rather than just in development—you avoid resource exhaustion or database overload and ensure a fast, reliable user experience.

Read Full Story
Multitenancy and .net DbContext

Multitenancy and .net DbContext

Read Full Story
Caching in Azure DevOps pipeline

Caching in Azure DevOps pipeline

Using caching in Azure DevOps pipelines can drastically reduce build times.I cut build time from 7-8 minutes to 2-3 minutes by implementing caching.

Read Full Story
© Andreas Skeie Hjelle 2025