What are the differences between the classic transaction pattern in LINQ to SQL like:
using(var context = Domain.Instance.GetContext())
{
try
{
context.Connection.Open();
context.Transaction = context.Connection.BeginTransaction();
/*code*/
context.Transaction.Commit();
}
catch
{
context.Transaction.Rollback();
}
}
vs the TransactionScope object
using (var context = Domain.Instance.GetContext())
using (var scope = new TransactionScope())
{
try
{
/*code*/
scope.Complete();
}
catch
{
}
}
It should be noted that when using the TransactionScope
there is no need for the try/catch
construct you have. You simply have to call Complete
on the scope in order to commit the transaction when the scope is exited.
That being said, TransactionScope
is usually a better choice because it allows you to nest calls to other methods that might require a transaction without you having to pass the transaction state around.
When calling BeginTransaction
on the DbConnection
object, you have to pass that transaction object around if you want to perform other operations in the same transaction, but in a different method.
With TransactionScope
, as long as the scope exists, it will handle everything that registers with the current Transaction
on the thread, making your code cleaner, and more maintainable.
On top of that, you have the added benefit of being able to use other resources that can participate in transactions, not just the connection to the database.
It should be noted that in situations where you need to squeeze the most out of your connections and database operations, you might not want to use TransactionScope
; even against a single database, you run the possibility of the Distributed Transaction Coordinator being used and having the transaction being turned into a distributed transaction (even for a single database connection).
In these cases, while muddying up your design, you might want to consider passing a connection-specific transaction around.
Or, if you know you will use one resource consistently (and on the same thread), you might want to create a class that reference-counts your connection/transaction.
You would create a class that on construction, creates your resource/increments the count. It would also implement IDisposable
(in which you would decrement/release/commit/abort when the count is zero), and store the count in a variable that has ThreadStaticAttribute
applied to it.
This allows you to separate the transaction management from the logic code, and still hold onto a singular resource fairly efficiently (instead of escalating to a distributed transaction).