Before .NET 7, Entity Framework Core was notoriously terrible at bulk operations. If you wanted to delete 10,000 expired logs, you had to write a LINQ query to download all 10,000 logs into C# memory, track them, mark them for deletion, and wait while EF Core fired 10,000 individual DELETE statements. With the introduction of ExecuteUpdateAsync and ExecuteDeleteAsync, EF Core finally processes bulk operations natively and instantly.
Instead of downloading the data, you write a standard LINQ Where clause and append the new execution method. This translates into a single SQL statement that executes immediately, bypassing the ChangeTracker entirely.
// Old Way (Crashes your server's RAM):
// var oldData = _context.Logs.Where(l => l.Year < 2020).ToList();
// _context.Logs.RemoveRange(oldData);
// await _context.SaveChangesAsync();
// New Way (.NET 7+):
// Instantly fires: DELETE FROM Logs WHERE Year < 2020
int deletedCount = await _context.Logs
.Where(l => l.Year < 2020)
.ExecuteDeleteAsync();
Console.WriteLine($"Surgically destroyed {deletedCount} rows in 2 milliseconds.");
You can perform massive mathematical updates across millions of rows directly inside the SQL engine utilizing lambda expressions.
// Scenario: Give all Active Employees a 5% raise.
// Instantly fires: UPDATE Employees SET Salary = Salary * 1.05 WHERE Status = 'Active'
int updatedCount = await _context.Employees
.Where(e => e.Status == "Active")
.ExecuteUpdateAsync(setter => setter
// The property to change , The mathematical SQL logic to apply
.SetProperty(e => e.Salary, e => e.Salary * 1.05m)
// You can chain multiple properties!
.SetProperty(e => e.LastUpdated, DateTime.UtcNow)
);
Because these bulk methods execute immediately against the database, they DO NOT interact with the ChangeTracker, and they DO NOT wait for SaveChanges().
Employee object with ID=5 loaded into your web request's RAM, and in the next line of code you use ExecuteUpdateAsync to fire them... your C# object in RAM will still show them as "Active". The ChangeTracker is not notified of the bulk update! You will have stale data.
Q: "If `ExecuteDeleteAsync` and `ExecuteUpdateAsync` are significantly faster than `SaveChanges()`, why wouldn't we just abandon the Change Tracker altogether and use these methods for every single CUD operation in our architecture?"
Architect Answer: "While bulk execution methods are phenomenally fast for set-based operations (affecting thousands of rows), they are detrimental when applied to complex Domain-Driven workflows. The ChangeTracker provides incredible benefits: It automatically orchestrates complex object graphs (inserting an Order and its 5 Line Items inside a single transaction), handles Concurrency Token checking to prevent multi-user overwrites, and executes essential interceptors (like auditing who changed the data). `ExecuteUpdateAsync` blindly executes the SQL, completely skipping all EF Core Interceptors, Triggers, and Concurrency validations. We use `SaveChanges` for complex business entity logic, and `ExecuteUpdate` exclusively for raw data janitorial tasks."