Another benefit of using Transactions is that you can group calls together, if somethign happens to one call then all of calls in that group can also be rolled back. This type of feature is particularly useful when taking user input and saving to DB, you may not want anything to get written to the DB is some error occurs on the client.
.NET provides us with the System.Transaction assembly, it's not completly straight forward to use but it does hide alot of the underlying complexities.
A couple of things I've learned about using System.Transactions or more specifically the System.Transactions.TransactionScope class:
- The safest way to use it is with the using clause.
- Always call Commit() before the end of the using.
- The optional TransactionScopeOptions enum parameter in the constructor defaults to TransactionScopeOption.Required meaning this new Transaction will join an existing one, the existing one is one that has already been created and is know as the "Ambiant Transaction".
TransactionOptions options = new TransactionOptions();
options.IsolationLevel = IsolationLevel.ReadCommitted;
options.Timeout = TimeSpan.FromMinutes(1);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
The different States of the Transaction
If the Transaction is not Completed it will rollback in code this means if you do not explicitly call TransactionScope.Complete() on the Transaction it will rollback.
So if you detect something wrong has happened in your code then do not call Complete() on it. You must dispose of the Transaction too, if you have your instance wrapped in a Using statement then this is done for you, else you can explicitly call it with the Dispose() method.
If Commit() has not been called before Dispose() then the Transaction goes into a state of Aborted. This indicates something has gone wrong inside the Transaction as Dispose() was called without a Complete().
You may not call Complete() after this, else you'll get a TransactionAbortedException.
So the rule is always either explicitly call Dispose() or let the Using block take care of it and if your code has not thrown an error then call Complete() before Dispose() is called else the functionality inside the Transaction will be rolled back.
What can be put inside a TransactionScope for commital/rollback?
You can put any code you like inside the using of the TransactionScope but only this object that support Transactions will be Rolledback or committed. Objects that support Transactions are usually objects that inherit from System.Transaction e.g.
The official wording from MS indicates that the object must be be able to support transaction promotion i.e. in .NET 2.0 there is something known as the the Lightweight Transaction Manager (LTM) which runs the transactions, it only manages objects that support transaction promotion, I think this actually means the objects implement a set of interfaces or inherit form System.Transaction.Transaction.
"Developers get access to this capability through new classes and interfaces within the System.Transactions namespace. For example, to explicitly start a transaction, an application can instantiate a new TransactionScope. If the application code for that transaction runs inside a single app domain, and if it involves at most a single durable resource, and if that resource supports transaction promotion, then the LTM can coordinate the transaction. For a transaction that involves application code that runs in multiple app domains (including multi-process and multi-machine scenarios), or for any transaction that involves more than one durable resource, even when all application code resides in a single app domain, or when a single transactional resource is involved but the resource does not support transaction promotion, the distributed (OleTx) transaction manager will be used. The application code itself need not concern itself with these optimizations—they just work."
What manages the Transactions?
The Lightweight Transaction Manager (LTM) manages the transactions.