MNW #013: How To Use The New Bulk Update Feature In EF Core 7

Nov 26 2022

5 min read

A big thank you to this week's sponsor who helps 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.

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
    .Where(n => !n.Snoozed)

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


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:

    .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:

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:

    .Where(n => n.Snoozed)

And EF7 will generate the following SQL query:

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 3 ways I can help you 🔥

  1. To access all of the source code that I use in my YouTube videos support me on Patreon
  2. For more practical & in-depth coding examples subscribe to my YouTube channel
  3. Promote yourself to 11k+ subscribers by sponsoring this newsletter (booked out 2 months)

Special Offers 📢

About the Newsletter

11k+ subscribers get one tip to improve their software engineering and .NET skills every Saturday morning.

Connect with Me

Join 11,774 .NET engineers getting 1 actionable .NET tip every Saturday

Actionable tips

Easy to implement

5 minute read

Free, forever