ExecuteUpdate and ExecuteDelete in EF Core – Faster Bulk Operations Without Loading Entities

Article Sponsors

EF Core too slow? Insert data up to 14x faster and cut save time by 94%. Boost performance with extension methods fully integrated into EF Core — Bulk Insert, Update, Delete, and Merge.

Join 5,000+ developers who’ve trusted our library since 2014.

👉 Try it now — and feel the difference

When working with databases in EF Core, updating or deleting large amounts of data has always required loading entities into memory first. This approach works, but it is not efficient—especially when dealing with thousands of records.

To solve this, EF Core introduced two powerful methods:

  • ExecuteUpdate
  • ExecuteDelete

These methods allow direct database operations without loading entities into memory, making them faster and more efficient.

The Problem with Traditional Updates and Deletes in EF Core

Before these methods, updating or deleting records typically looked like this:

C#
var customers = await dbContext.Customers
    .Where(c => c.IsInactive)
    .ToListAsync();

dbContext.Customers.RemoveRange(customers);

await dbContext.SaveChangesAsync();

Problems with this approach:

  • Loads data into memory
  • Uses change tracking (extra overhead)
  • Multiple database round-trips
  • Slower for large datasets

What Are ExecuteUpdate and ExecuteDelete?

ExecuteUpdate and ExecuteDelete allow running SQL-like operations directly on the database.

  • They do not load entities
  • They do not use change tracking
  • They execute as a single SQL query

Think of them as EF Core’s way of writing efficient bulk operations using LINQ.

Why These Methods Matter

These methods bring major improvements:

  • Better performance – no unnecessary data loading
  • Low memory usage – avoids tracking entities
  • Single query execution – reduces database calls
  • Cleaner code – no loops or manual processing

How ExecuteUpdate Works

Use ExecuteUpdate to update records directly in the database.

Example: Increase price by 10%

C#
await dbContext.Products
    .Where(p => p.Category == "Electronics")
    .ExecuteUpdateAsync(setters => setters
        .SetProperty(p => p.Price, p => p.Price * 1.10));

What happens:

  • Executes a single UPDATE query
  • Applies changes to all matching records
  • No entity tracking involved

How ExecuteDelete Works

Use ExecuteDelete when you want to delete records directly in the database.

Example: Delete inactive customers

C#
await dbContext.Customers
    .Where(c => c.IsInactive)
    .ExecuteDeleteAsync();

What happens here:

  • EF Core translates this into a single DELETE SQL query
  • No data is loaded into memory
  • No SaveChanges() is required
EF Core performance optimization sponsor banner showing bulk insert, update, delete, and merge features with 14x faster data operations.

Filtering Data Before Executing Operations

Both methods work with LINQ filters.

Example: Delete old orders

C#
await dbContext.Orders
    .Where(o => o.CreatedDate < DateTime.UtcNow.AddYears(-1))
    .ExecuteDeleteAsync();

Only matching records will be affected.

Updating Multiple Fields in One Query

You can update multiple columns in one operation.

C#
await dbContext.Employees
    .Where(e => e.IsActive)
    .ExecuteUpdateAsync(setters => setters
        .SetProperty(e => e.Salary, e => e.Salary + 5000)
        .SetProperty(e => e.UpdatedAt, e => DateTime.UtcNow));

Multiple fields updated in a single database call.

Change Tracking Behavior

One important thing to understand is how change tracking works with these methods.

  • EF Core does NOT track changes
  • Tracked entities in memory are NOT updated automatically

If entities are already loaded:

C#
var customer = await dbContext.Customers.FirstAsync();

await dbContext.Customers
    .Where(c => c.Id == customer.Id)
    .ExecuteUpdateAsync(s => s.SetProperty(c => c.Name, "Updated"));

The customer object in memory will still have the old value.

Always be careful when mixing tracked entities with these methods.

Transaction Behavior

By default, each ExecuteUpdate or ExecuteDelete runs as a single database command.

Important points:

  • Each call is executed immediately
  • No need for SaveChanges()
  • Runs in its own transaction (depending on provider)

If multiple operations need to be consistent:

C#
using var transaction = await dbContext.Database.BeginTransactionAsync();

await dbContext.Orders
    .Where(o => o.IsExpired)
    .ExecuteDeleteAsync();

await dbContext.Products
    .Where(p => p.Stock == 0)
    .ExecuteUpdateAsync(s => s.SetProperty(p => p.IsAvailable, false));

await transaction.CommitAsync();

This ensures all operations succeed or fail together.

Things to Keep in Mind

  • No change tracking
  • No entity lifecycle events (like SavingChanges)
  • No navigation property updates
  • Immediate execution (no SaveChanges())

When to Use ExecuteUpdate and ExecuteDelete

These methods are ideal when:

  • Working with large datasets
  • Performing bulk updates or deletes
  • Optimizing performance-critical operations
Good use cases:
  • Archiving old data
  • Soft deletes
  • Bulk status updates
  • Scheduled cleanup jobs
Avoid when:
  • Business logic depends on entity tracking
  • Complex domain rules must run per entity

Limitations

While ExecuteUpdate and ExecuteDelete are powerful, there are a few limitations to be aware of:

  • No insert support – These methods only support update and delete operations. For inserting data, still use: DbSet<TEntity>.Add and SaveChanges().
  • No access to affected row data – Even though SQL allows returning updated or deleted values, EF Core currently does not support retrieving those values with these methods.
  • No batching across multiple calls – Each call to ExecuteUpdate or ExecuteDelete runs as a separate database roundtrip. Multiple calls are not combined into one query.
  • Single table operations only – Most databases only allow updating or deleting from one table at a time, and these methods follow that rule.
  • Relational databases only – These features work only with relational database providers (like SQL Server, PostgreSQL, MySQL). They are not supported in non-relational providers.

Summary

ExecuteUpdate and ExecuteDelete are powerful additions to EF Core that simplify bulk operations and improve performance.

Instead of loading data into memory, these methods execute operations directly in the database using a single query. This makes them ideal for high-performance applications.

Takeaways

  • ExecuteUpdate and ExecuteDelete avoid loading entities
  • They execute as a single SQL query
  • No need for SaveChanges()
  • Change tracking is bypassed
  • Can be wrapped in transactions when needed
  • Best for bulk operations and performance optimization
This article is sponsored by ZZZ Projects.

Thousands of developers fixed EF Core performance — with one library: Entity Framework Extensions.

👉 Insert data 14x faster with Bulk Insert

Found this article useful? Share it with your network and spark a conversation.