Chapter 14 · Transactions, Savepoints, Execution Strategies, and Cross-Context Coordination

Savepoints and Automatic Rollback Inside Existing Transactions

Use automatic and explicit savepoints, prove partial rollback behavior, and reconcile EF tracked state after the database moves back to a savepoint.

Advanced135–175 minutessavepoint + tracker-reconciliation labEF Core 10.0.11 · .NET 10.0.11SQLite provider 10.0.11 mandatory baselineSDK checkpoint: 10.0.400Last reviewed: August 2026

Learning outcomes

A savepoint is a named recovery position inside an already-open transaction. Rolling back to a savepoint keeps the outer transaction alive while undoing database commands after that point. EF Core can create savepoints automatically around SaveChanges when a transaction is already in progress, and relational transaction APIs also expose explicit savepoint operations.

01

Explain EF automatic savepoints when SaveChanges runs inside an existing transaction.

02

Use CreateSavepointAsync, RollbackToSavepointAsync, and ReleaseSavepointAsync deliberately.

03

Observe that database rollback does not rewind CLR object/current-value state automatically.

04

Reconcile ChangeTracker state after rolling back a previously successful SaveChanges.

05

Distinguish savepoint support from fake nested transactions and provider limitations.

06

Explain why SQL Server MARS disables EF automatic savepoint creation.

Reproducible baseline

Mandatory labs use .NET SDK 10.0.400, .NET runtime 10.0.11, Microsoft.EntityFrameworkCore/SQLite 10.0.11, dotnet-ef 10.0.11, the disposable servicehub-lab.db, deterministic ServiceHub seed data, the existing Guid Revision concurrency token, and the Chapter 12 outbox teaching model when referenced. SQL Server retry/MARS/System.Transactions examples are optional provider-specific paths. EF Core 11 previews are excluded.

1. Why a savepoint exists

Suppose one ServiceHub transaction performs phase A successfully, then phase B hits a recoverable problem. Rolling back the entire transaction would discard A; committing after B fails would be unsafe. A savepoint gives the database a checkpoint inside the transaction.

Operation Outer transaction Database changes before savepoint Changes after savepoint
Create savepoint remains active kept future work begins
Rollback to savepoint remains active kept undone
Release savepoint remains active kept checkpoint name/resources released
Rollback transaction ends/aborts undone undone

2. EF automatic savepoint before SaveChanges

When SaveChanges executes while an EF transaction is already active, EF normally creates a savepoint first. If that save fails, EF rolls the database transaction back to that savepoint so the outer transaction can potentially continue. This is especially useful around optimistic concurrency recovery.

SQL Server MARS warning

EF documents that savepoints are incompatible with SQL Server Multiple Active Result Sets (MARS). When MARS is enabled, EF does not create automatic savepoints—even if no second result set is active. A failed SaveChanges can therefore leave the transaction in an unknown state; do not enable MARS casually in code that relies on savepoint recovery.

3. Explicit savepoint: prove partial database rollback

csharp · two successful saves, then rollback the second
await using var tx = await db.Database.BeginTransactionAsync(ct);var order = await db.WorkOrders.SingleAsync(x => x.Id == id, ct);order.ReviseSummary("phase A: diagnostics approved");order.AdvanceRevision();await db.SaveChangesAsync(ct);await tx.CreateSavepointAsync("AfterPhaseA", ct);order.ReviseSummary("phase B: replacement approved");order.AdvanceRevision();await db.SaveChangesAsync(ct);await tx.RollbackToSavepointAsync("AfterPhaseA", ct);

At the database level, phase B is undone and phase A remains inside the still-open transaction. But the tracked order object has already passed through a successful SaveChanges for phase B, so its CLR/current/original values may describe phase B while the database is back at phase A.

4. Deliberately wrong: assume rollback rewinds ChangeTracker

csharp · stale in-memory state after rollback
await tx.RollbackToSavepointAsync("AfterPhaseA", ct);Console.WriteLine(order.Summary);             // may still be the phase-B CLR valueConsole.WriteLine(db.Entry(order).State);     // may be Unchanged after prior SaveChangesawait tx.CommitAsync(ct);                     // database commits phase A, not CLR phase B

A database transaction owns database state; EF’s state manager owns CLR tracking state. Rolling the database back does not perform a time-travel operation on your object graph.

Repair explicitly

After savepoint rollback, decide what state should continue. Reload affected entries from the database, restore known original/current values, or discard the context and create a new one if the graph is complex. Do not keep writing based on tracker state you have not reconciled.

5. Reconcile and continue

csharp · reload after rollback
await tx.RollbackToSavepointAsync("AfterPhaseA", ct);await db.Entry(order).ReloadAsync(ct);Console.WriteLine(order.Summary); // phase A value visible inside this transactionorder.ReviseSummary("phase B revised after validation");order.AdvanceRevision();await db.SaveChangesAsync(ct);await tx.ReleaseSavepointAsync("AfterPhaseA", ct);await tx.CommitAsync(ct);

Reload issues a database query using the current transaction/connection and aligns the entity with the rolled-back database state before a new proposal is applied. For multiple related entries, reconcile the entire affected graph or restart with a fresh context rather than selectively reloading one row and assuming relationships are coherent.

6. Savepoint support is provider capability

csharp · inspect underlying ADO.NET capability
var adoTransaction = tx.GetDbTransaction();Console.WriteLine(adoTransaction.SupportsSavepoints);

Microsoft.Data.Sqlite supports savepoints. Other providers may differ or impose restrictions. EF's relational API surface does not guarantee that every engine/topology implements the same behavior. Handle NotSupportedException as a capability mismatch during development/configuration—not as a reason to silently drop recovery semantics in production.

7. Automatic rollback on a failed save does not decide business recovery

EF can return the database to the pre-savepoint state after a failed SaveChanges, but the application still has to classify the failure. A concurrency conflict may be mergeable; a unique/check constraint violation may require different input; a lock/timeout may be transient; cancellation should normally abort the workflow. Savepoints preserve options—they do not choose policy.

Failure after savepoint Database recovery Tracker/application next step
DbUpdateConcurrencyException EF can roll back failed save to automatic savepoint Resolve/reload/merge and retry within bounded policy.
Constraint violation failed SaveChanges rolled back Fix invalid proposal or abort; do not generic-retry unchanged input.
Provider transient depends on provider/transaction state Execution strategy rules apply; entire user transaction may need replay.
Cancellation abort requested work Rollback/dispose transaction and propagate cancellation.

8. Hands-on lab

  1. Reset SQLite and start one explicit transaction.
  2. Save phase A and create AfterPhaseA.
  3. Save phase B, capture ChangeTracker.DebugView.LongView, then roll back to the savepoint.
  4. Query phase A with a raw scalar/read inside the transaction and compare it to the still-tracked CLR value.
  5. Call ReloadAsync; verify current/original values now match phase A.
  6. Apply a revised phase B, save, commit, then verify from a fresh context.
  7. Print SupportsSavepoints.
  8. For the optional SQL Server path, document why enabling MARS changes EF automatic-savepoint behavior.

Check your understanding

  1. Does rollback to a savepoint end the outer transaction?
  2. Does RollbackToSavepointAsync rewind CLR properties automatically?
  3. When does EF normally create an automatic savepoint?
  4. What SQL Server setting disables EF automatic savepoints?
  5. Why might discarding the context be safer after a complex rollback?
  6. Is a savepoint a second independent transaction?
Review the answers

No. It returns database state to the savepoint and keeps the transaction active.

No. Reconcile the ChangeTracker/object graph explicitly.

Before SaveChanges when a transaction is already active on the context, subject to provider limitations.

Multiple Active Result Sets (MARS).

A large graph may contain stale current/original/entity/relationship state that is difficult to reconcile correctly entry by entry.

No. It is a recovery marker inside the same outer transaction.

9. Production judgment and bridge

Savepoints make long multi-step local transactions more recoverable, but they also increase state-management responsibility. Keep transaction work small, reconcile trackers deliberately, and test provider limitations. Lesson 3 adds another replay boundary: retrying execution strategies, where the whole transaction delegate—not merely one failed command—may need to run again.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.

\n