Entity Framework Performance

In depth analysis of the Entity Framework

The Entity Framework is a fantastic tool that reduces development time on new projects. While it used to take a significant amount of time to write a data access layer, the Entity Framework now handles much of this for you. There are many good reasons to use the Entity Framework, but it requires some care to use properly; poor use can result in substantial performance degradation. I’ve been able to distil the majority of what a developer needs to know about EF performance down to three concepts.

 

I recently started working on an old system that had a certain background process that runs several times a day. As a result of ever-increasing amounts of data, that process was beginning to be unacceptably slow (i.e. 30-60 minutes, or more in some cases). Using just the few methods outlined below, I was able to get the process to run in a mere 10% of the time that it took before: 30-60 minutes => 3-6 minutes. If this says anything, it’s that the EF makes it effortless to shoot yourself in the foot if you’re not careful.

So let’s get into some details:

1. Disable Auto Detection of Changes

The Entity Framework needs to know how entities have changed, to know what updates need saving to the database. It determines the changes by comparing current entity property values with original values. This change detection algorithm gets run in several scenarios, with the most common being when the user’s code calls the following methods:

  • DbSet.Find()
  • DbSet.Remove()
  • DbSet.Add()
  • DbSet.Attach()
  • DbContext.SaveChanges()

What you need to know is that if your context is tracking a lot of entities, then change detection can become a costly operation. This is especially true if one of these methods gets called within a for() loop. For example: if you add many entities to the context in a loop, you will notice a significant performance degradation due to change detection.

Now the good news is that change detection is easy to disable, but you’ll also have to remember to turn it back on. The best way to do this is to:

  1. Wrap your offending code in a try/finally block.
  2. At the beginning of the try block, disable auto change detection withcontext.Configuration.AutoDetectChangesEnabled = false;
  3. In your finally block (before saving changes), re-enable it withcontext.Configuration.AutoDetectChangesEnabled = true;

Note that this will only provide significant performance improvements with larger collections (i.e. > 100 items), so in most cases it’s simpler to just leave it on.

For more performance details, see Entity Framework Performance and AutoDetectChanges

2. Use Projections

Whenever running a query with the Entity Framework, it’s important to ask yourself what you plan on doing with that data. If you’re only going to be reading data (and not modifying anything), then you will be able to use projections to get some performance gains.

When the Entity Framework runs a query, it materializes the results into Entities that get cached by the DbContext When using a projection to select anonymous objects or another POCO type, however, you get two big benefits:

  1. The results aren’t cached by the DbContext, so you get better performance due to less overhead.
  2. Rather than running aSELECT * FROM …, it instead selects only the specific columns that you need. This means less data gets returned from your database and stored in memory.

3. Disable Change Tracking

You may occasionally find yourself in a situation where you only need to read data, but you still want the actual entities. Fortunately, there’s an easy way to tell the Entity Framework that you don’t want it to cache the results of a query or track changes on the resultant entities. Calling context.CustomerOrders.AsNoTracking() instructs the EF to get all the customer orders, but not track or cache the results. This gets you some additional performance gains when dealing with large amounts of data.

Frankly, the only reason I can think of to use .AsNoTracking() ather than just using a projection is that you’re dealing with legacy code and you don’t want to refactor everything in sight.

Now, a few things to keep in mind:

  1. These are not the only ways to get better performance out of the Entity Framework: just the best ones.
  2. You may have a less general case that requires some additional performance tuning.
  3. There are some cases where EF performance will never be close what you get from stored procedures or straight SQL queries, so be sure to use each tool for their strengths.

Happy performance tuning!

You may also like

Leave a comment