How To Use The New Bulk Update Feature In EF Core 7

How To Use The New Bulk Update Feature In EF Core 7

4 min read ·

Thank you to our sponsors who keep this newsletter free to the reader:

Sick of staying up late firefighting deployments? Reclaim your nights with a database that keeps your app up even when things go sideways. Outsource hosting to the experts with RavenDB Cloud today so you can sleep well tomorrow.

In this week's newsletter, we're going to explore the new ExecuteUpdate and ExecuteDelete methods that were released with EF7.

ExecuteUpdate allows us to write a query and run a bulk update operation on the entities matching that query.

Similarly, ExecuteDelete allows us to write a query and delete the entities matching that query.

We can significantly improve performance using the new methods in some scenarios, and I'm going to show you what those scenarios are.

Let's dive in.

Updating And Deleting Entities Before EF Core 7

If you want to update a collection of entities before EF7, you need to load the entities into memory using the DatabaseContext.

The EF ChangeTracker will then track any changes made to these entities. When you are ready to commit the changes to the database, you simply call the SaveChanges method.

Here's an example where we load a few notifications, and we want to snooze them so they aren't sent:

var notifications = dbContext
    .Notifications
    .Where(n => !n.Snoozed)
    .ToList();

foreach(var notification in notifications)
{
    notification.Snoozed = true;
}

dbContext.SaveChanges();

EF7 will generate the following SQL statement to update the records in the database:

UPDATE [Notifications] n
SET n.[Snoozed] = TRUE
WHERE n.[Id] = @notificationId_1;

...

UPDATE [Notifications] n
SET n.[Snoozed] = TRUE
WHERE n.[Id] = @notificationId_N;

Notice that for every notification we end up with one SQL UPDATE statement. This won't scale well as the number of notifications increases.

Updating Entities With ExecuteUpdate

With EF7, we now have access to the new ExecuteUdpate method. It also has an asynchronous version - ExecuteUpdateAsync.

How do you use it?

You need to write a query that will select the records you want to update, and then call the ExecuteUpdate method on the resulting IQueryable.

Let's rewrite the previous example using the new approach:

dbContext
    .Notifications
    .Where(n => !n.Snoozed)
    .ExecuteUpdate(s => s.SetProperty(
        n => n.Snoozed,
        n => true));

In the call to ExecuteUpdate we call the SetProperty method to specify which properties we want to update, and what values we want to set. The SetProperty method can be called multiple times, if you need to update more than one property.

In this case, EF7 will generate the following SQL query:

UPDATE n
SET n.[Snoozed] = TRUE
FROM [Notifications] AS n
WHERE n.[Snoozed] = FALSE;

Notice that this time we only have one SQL query being sent to the database. This is a major performance improvement. It can be as much as 10x faster than the old version, from my testing.

Deleting Entities With ExecuteDelete

Let's also see how we can do bulk deletes using the ExecuteDelete and ExecuteDeleteAsync methods.

Again, you have to write a query that will select the records you want to delete, and then call the ExecuteDelete method on the resulting IQueryable.

If you want to delete all snoozed notifications:

dbContext
    .Notifications
    .Where(n => n.Snoozed)
    .ExecuteDelete();

And EF7 will generate the following SQL query:

DELETE FROM n
FROM [Notifications] AS n
WHERE n.[Snoozed] = TRUE;

I think this will be incredibly useful when you want to delete records in the database based on a specific condition.

Transactions, Change Tracking And Query Filters With Bulk Methods

You need to be aware how transactions and change tracking work with the new bulk methods. ExecuteUpdate and ExecuteDelete will immediately go to database, and run the SQL query.

What does this mean for transactions?

If you want to run a bulk method together with other updates applied with SaveChanges, by default they won't run in the same transaction. You need to open an explicit transaction using the DatabaseContext to keep everything consistent.

What does this mean for change tracking?

ExecuteUpdate and ExecuteDelete run directly on the database, without loading any entities into memory. EF7 will not track these entities in the ChangeTracker.

If you have any database interceptors defined, they won't execute after calling one of the bulk update methods. This also means that if you override SaveChanges to add custom behavior, it won't be called.

Do Query Filters still work?

Yes, query filters will be correctly applied when calling ExecuteUpdate or ExecuteDelete.

When Should You Use The New Bulk Methods?

I think this is an excellent new addition to EF7, and it solves a real problem when you need to run a typical UPDATE or DELETE query with a WHERE statement applied.

Previously, you had to write raw SQL and execute it using something like Dapper.

I will likely use this approach when it applies to my projects.


Whenever you're ready, there are 4 ways I can help you:

  1. Modular Monolith Architecture (COMING SOON): This in-depth course will transform the way you build monolith systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario. Join the waitlist here.
  2. Pragmatic Clean Architecture: This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. Join 2,600+ students here.
  3. Patreon Community: Join a community of 1,050+ engineers and gain access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses. Join 1,050+ engineers here.
  4. Promote yourself to 46,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 46,000+ engineers who are improving their skills every Saturday morning.