TransactionScope vs Transaction in LINQ to SQL

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
{
}
}
62233 次浏览

I believe they are fundamentally the same that the TransactionScope class will interface with the ADO.NET underlying connection to create and either commit or rollback the transaction. That the TransactionScope class was just created to make working with ADO.NET persistence cleaner.

Edit: Clarifying my statement with regards to casperOne's addition it is the TransactionScope that will create the transaction and the connection will then see the transaction that was created by the TransactionScope and use it since it's available to it.

Linq2SQL will use an implicit transaction. If all of your updates are done within a single Submit, you may not need to handle the transaction yourself.

From the documentation (emphasis mine):

When you call SubmitChanges, LINQ to SQL checks to see whether the call is in the scope of a Transaction or if the Transaction property (IDbTransaction) is set to a user-started local transaction. If it finds neither transaction, LINQ to SQL starts a local transaction (IDbTransaction) and uses it to execute the generated SQL commands. When all SQL commands have been successfully completed, LINQ to SQL commits the local transaction and returns.

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

One big difference (lesson learnt the hard way) – TransactionScope uses MS DTC for transaction management.

If your application has to manage database transaction only, and no services or remote calls are involved, you can skip the potential issues related to MS DTC by using transaction native to databases (DbTransactions).

TransactionScope supplies unified management for all resource mangers (SQL server, active directory, file system, …). Moreover, one can write own resource manager: code that detects transaction scope, join its and works exactly as SQL server does: commits or reverts changes like other participants of the transaction. I believed that TransactionScope is mainstream and forgot MS SQL native transactions until failed into huge trap: Windows Server 2008 WEB Edition comes with restricted Distributed Transaction Coordinator Service and Transaction scope works on single computer only. Your ASP.NET application will fail on this system if IIS and SQL server are installed on different computers. Take into account that most public domain providers supply Windows Server WEB edition and SQL server are on separate servers. This means, that you must work with native transactions using explicit transactions management …