In this lesson, you will learn how to modify or remove large sets of data directly in your database without the performance tax of loading every entity into your application's memory.
The Problem with Traditional Updates
Usually, when you want to update records in Entity Framework Core, you retrieve them using a query, modify their properties, and call SaveChanges(). EF Core tracks these changes and generates individual UPDATE or DELETE statements for every single record. If you need to update 10,000 "Pending" orders to "Cancelled," loading 10,000 objects into your DbContext tracking cache will consume significant memory and result in 10,000 round-trips to your database.
The Bulk Operation Approach
To handle high-volume data operations efficiently, we use bulk commands. These commands translate into a single UPDATE or DELETE statement executed directly on the database engine. Instead of iterating through entities, you issue a declarative command that tells the database exactly what to change.
Consider this example using the ExecuteUpdateAsync method:
// Update all orders that are 'Pending' to 'Cancelled' in one go
await context.Orders
.Where(o => o.Status == "Pending")
.ExecuteUpdateAsync(setters => setters
.SetProperty(o => o.Status, "Cancelled")
.SetProperty(o => o.LastModified, DateTime.UtcNow));
Similarly, you can perform bulk deletions:
// Delete all logs older than 30 days
await context.Logs
.Where(l => l.Timestamp < DateTime.UtcNow.AddDays(-30))
.ExecuteDeleteAsync();
Why This Matters
By using these methods, you bypass the DbContext change tracker entirely. Because the operation happens on the server side, your application doesn't need to materialize the records into C# objects. This dramatically reduces memory pressure and network latency.
However, keep in mind that since these operations skip the change tracker, they do not trigger EF Core events (like SavingChanges) and do not update any entities currently being tracked in your ChangeTracker. Always use these for high-performance batch scenarios where granular tracking is not required.
Key Takeaway: Use ExecuteUpdateAsync and ExecuteDeleteAsync to perform database-side operations, significantly improving performance by avoiding the overhead of loading and tracking entities in memory.