Transactions in .net
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Transactions in .NET exist to keep a group of operations consistent: either all succeed or all are rolled back. The practical challenge is choosing the right transaction API for the level you are working at, such as raw ADO.NET, TransactionScope, or an ORM like EF Core. The best choice depends on how many resources are involved and how explicit you need the control to be.
What a Transaction Guarantees
A transaction gives you the classic ACID guarantees:
- atomicity
- consistency
- isolation
- durability
In application code, the two most visible guarantees are:
- if one step fails, earlier writes are rolled back
- concurrent work sees a controlled view of in-progress changes
Without transactions, partial updates are easy to create and hard to repair.
ADO.NET SqlTransaction
When you are using raw SQL commands against one database connection, SqlTransaction is the most direct tool.
This is explicit and predictable. It is a good default when the work is confined to one connection.
TransactionScope for Ambient Transactions
TransactionScope creates an ambient transaction that participating operations can join automatically.
If Complete() is not called, the transaction is rolled back when the scope is disposed.
This approach is convenient, but it can become harder to reason about if too much code participates implicitly.
EF Core Transaction Example
If you are using EF Core, you can either rely on SaveChanges() transaction behavior for simple units of work or open an explicit transaction for multi-step workflows.
This is the right pattern when multiple SaveChanges calls must succeed or fail together.
Async Code and Transactions
Async flows add an important detail: not every transaction API works correctly unless async flow is enabled or the ORM supports async transaction boundaries directly.
For TransactionScope, use:
Without it, async code can break ambient transaction flow in confusing ways.
For explicit ADO.NET or EF Core transactions, prefer the async transaction methods when the surrounding code is async.
Choose the Right Isolation Level
Isolation level affects both correctness and concurrency.
Typical choices:
- '
ReadCommitted: common default' - '
RepeatableRead: stronger consistency for repeated reads' - '
Serializable: strongest isolation, highest contention risk'
Do not increase isolation casually. Higher isolation can reduce concurrency and create lock contention under load.
Distributed Transactions Need Caution
If one logical unit of work touches multiple resource managers, such as multiple databases or a database plus a message queue, ambient transactions can become distributed transactions. Those add operational complexity and may not be supported everywhere.
In modern systems, many teams avoid this by:
- using an outbox pattern
- separating operations into local transactions
- designing for eventual consistency where appropriate
Do not reach for distributed transactions unless the consistency requirement clearly justifies them.
Common Failure Pattern
A typical bug looks like this:
That is exactly the scenario transactions are meant to prevent. If the operations belong to one business action, they should usually live in one transaction boundary.
Common Pitfalls
One common mistake is making the transaction scope too large, such as wrapping slow network calls together with database writes. That increases lock time and reduces throughput.
Another mistake is forgetting to call Complete() on TransactionScope, which results in rollback even when the code appears to succeed.
Developers also mix multiple connections and ORMs without understanding whether they are joining the same transaction context.
Finally, using transactions for every read path can add unnecessary cost. Not every operation needs a custom transaction boundary.
Summary
- Use transactions when several operations must succeed or fail as one unit.
- Prefer
SqlTransactionfor explicit one-connection ADO.NET workflows. - Use
TransactionScopecarefully when ambient transaction flow is helpful. - In EF Core, use explicit database transactions for multi-step save workflows.
- Keep transaction boundaries small and choose isolation deliberately.
Related reading
- Transactions in spring boot testing not rolled back
- Transactions with DynamoDB library Boto3
- TransactionScope automatically escalating to MSDTC on some machines?
- Transpose rows to columns in clickhouse
- Traversing a tree of objects in c
- Trim string from the end of a string in .NET - why is this missing?
- Trouble connecting to postgres from outside Kubernetes cluster
- Troubleshooting Illegal mix of collations error in mysql

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.