SaveChanges' Implicit Transaction
Every call to SaveChanges() that generates more than one SQL statement is already wrapped in an implicit database transaction by EF Core. If you add three new entities and modify two existing ones, EF batches all five statements inside a single BEGIN TRANSACTION / COMMIT block, so a failure partway through rolls everything back rather than leaving the database half-updated. This implicit behavior covers the common case, but it only spans a single SaveChanges call — if you need atomicity across multiple SaveChanges calls, or want to mix EF operations with raw ADO.NET commands, you need an explicit transaction.
Cricket analogy: It's like a team's entire fielding change during a bowling over being applied together — the umpire only signals play resumed once every fielder is in position, not partway through the reshuffle.
Explicit Transactions with BeginTransaction
When you need multiple SaveChanges calls, or a mix of EF LINQ operations and raw SQL via ExecuteSqlRaw, to succeed or fail together, you open an explicit transaction with context.Database.BeginTransaction() (or the async BeginTransactionAsync). Everything executed against that context — including subsequent SaveChanges calls — participates in the same transaction until you call transaction.Commit() or transaction.Rollback(). This is the pattern for workflows like 'debit one account, credit another, then log an audit record,' where any failure in step two must undo step one even though they were separate SaveChanges calls.
Cricket analogy: It's like a Duckworth-Lewis-Stern recalculation that spans an entire rain-affected innings — the revised target isn't finalized until every input (overs lost, wickets, par score) is confirmed together, or the whole recalculation is discarded.
Savepoints for Partial Rollback
Within a single explicit transaction, EF Core automatically creates savepoints around each SaveChanges call when the underlying provider supports them (SQL Server and PostgreSQL both do). A savepoint is a named marker inside the transaction that you can roll back to without discarding everything since the transaction began. If SaveChanges fails, EF automatically rolls back to the savepoint created just before that call, undoing only that batch's statements while preserving earlier successful work in the same transaction — you can then retry or handle the error and continue the transaction instead of being forced to abort it entirely.
Cricket analogy: It's like a batting partnership where only the most recent partnership resets after a wicket falls — the runs and overs from earlier partnerships in the innings stay on the board rather than the whole innings being scrubbed.
using var transaction = await context.Database.BeginTransactionAsync();
try
{
context.Accounts.Find(fromId).Balance -= amount;
await context.SaveChangesAsync(); // EF creates a savepoint here
context.Accounts.Find(toId).Balance += amount;
await context.SaveChangesAsync(); // and another savepoint here
context.AuditLogs.Add(new AuditLog { Message = "Transfer completed" });
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (DbUpdateException)
{
// EF automatically rolls back to the last savepoint on SaveChanges failure;
// Rollback here discards the whole transaction if you choose not to retry.
await transaction.RollbackAsync();
throw;
}
// Manual savepoints are also available directly:
await transaction.CreateSavepointAsync("BeforeRiskyStep");
// ... risky work ...
await transaction.RollbackToSavepointAsync("BeforeRiskyStep");
Not every provider supports savepoints — check context.Database.AutoSavepointsEnabled and the provider docs; SQLite, for example, has more limited savepoint support than SQL Server or PostgreSQL.
- SaveChanges() always wraps its own generated SQL statements in an implicit transaction, even without explicit code.
- Use context.Database.BeginTransactionAsync() when atomicity must span multiple SaveChanges calls or mix EF with raw SQL.
- An explicit transaction stays open until transaction.CommitAsync() or transaction.RollbackAsync() is called.
- EF Core automatically creates a savepoint before each SaveChanges call within an explicit transaction on providers that support them.
- A failed SaveChanges rolls back only to the most recent savepoint, preserving earlier successful work in the same transaction.
- You can also create and roll back to named savepoints manually via CreateSavepointAsync and RollbackToSavepointAsync.
- Savepoint support varies by provider — confirm compatibility before relying on partial rollback behavior.
Practice what you learned
1. Is a single call to SaveChanges() that generates multiple INSERT statements already transactional?
2. When do you need an explicit transaction via BeginTransaction()?
3. What does EF Core automatically create before each SaveChanges call inside an explicit transaction (on a supporting provider)?
4. If a SaveChanges call fails inside an explicit transaction with automatic savepoints enabled, what is rolled back by default?
5. Which methods let you manage savepoints manually?
Was this page helpful?
You May Also Like
Change Tracking Explained
How EF Core's ChangeTracker snapshots entity state and computes the minimal set of INSERT, UPDATE, and DELETE statements needed at SaveChanges time.
Optimistic Concurrency Control
How EF Core detects and resolves concurrent edit conflicts using concurrency tokens and DbUpdateConcurrencyException, and how this compares to pessimistic locking.
Tracking vs No-Tracking Queries
When to let EF Core track query results for later updates versus opting out with AsNoTracking for faster, read-only workloads.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics