TransactionScope vs Transaction in LINQ to SQL

Ben McNiel picture Ben McNiel · Feb 12, 2009 · Viewed 59.8k times · Source

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
    {
    }
}

Answer

casperOne picture casperOne · Feb 12, 2009

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).