Use System.Transactions
Applies to: SQL Server
The System.Transactions
namespace provides a transaction framework that is fully integrated with ADO.NET and SQL Server common language runtime (CLR) integration. The System.Transactions.TransactionScope
class makes a code block transactional by implicitly enlisting connections in a distributed transaction. You must call the Complete
method at the end of the code block marked by the TransactionScope
. The Dispose
method is invoked when program execution leaves a code block, causing the transaction to be discontinued if the Complete
method isn't called. If an exception was thrown that causes the code to leave scope, the transaction is considered to be discontinued.
We recommend that you employ a using
block to ensure that the Dispose
method is called on the TransactionScope
object when the using
block is exited. Failure to commit or roll back pending transactions can seriously degrade performance because the default time-out for the TransactionScope
is one minute. If you don't use a using
statement, you must perform all work in a Try
block and explicitly call the Dispose
method in the Finally
block.
If an exception occurs within the TransactionScope
, the transaction is marked as inconsistent and is abandoned. It rolls back when the TransactionScope
is disposed. If no exception occurs, participating transactions commit.
TransactionScope
should be used only when local and remote data sources or external resource managers are being accessed, because TransactionScope
always causes transactions to promote, even if it's being used only within a context connection.
The TransactionScope
class creates a transaction with a System.Transactions.Transaction.IsolationLevel
of Serializable
by default. Depending on your application, you might want to consider lowering the isolation level to avoid high contention in your application.
Note
We recommend that you perform only updates, inserts, and deletes within distributed transactions against remote servers because they consume significant database resources. If the operation is going to be performed on the local server, a distributed transaction isn't necessary and a local transaction will suffice. SELECT
statements might lock database resources unnecessarily, and in some scenarios it might be necessary to use transactions for selects. Any non-database work should be done outside of the scope of the transaction, unless it involves other transacted resource managers.
Although an exception within the scope of the transaction prevents the transaction from committing, the TransactionScope
class has no provision for rolling back any changes your code makes outside of the scope of the transaction itself. If you need to take some action when the transaction is rolled back, you must write your own implementation of the System.Transactions.IEnlistmentNotification
interface, and explicitly enlist in the transaction.
Examples
To work with System.Transactions
, you must have a reference to the System.Transactions.dll file.
The following code demonstrates how to create a transaction that can be promoted against two different instances of SQL Server. These instances are represented by two different System.Data.SqlClient.SqlConnection
objects, which are wrapped in a TransactionScope
block. The code creates the TransactionScope
block with a using
statement, and opens the first connection, which automatically enlists it in the TransactionScope
. The transaction is initially enlisted as a lightweight transaction, not a full distributed transaction. The code assumes the existence of conditional logic (omitted for brevity). It opens the second connection only if it's needed, enlisting it in the TransactionScope
.
When the connection is opened, the transaction is automatically promoted to a full distributed transaction. The code then invokes TransactionScope.Complete
, which commits the transaction. The code disposes of the two connections when exiting the using
statements for the connections. The TransactionScope.Dispose
method for the TransactionScope
is automatically called at the termination of the using
block for the TransactionScope
. If an exception was thrown at any point in the TransactionScope
block, Complete
doesn't get called, and the distributed transaction rolls back when the TransactionScope
is disposed.
using (TransactionScope transScope = new TransactionScope())
{
using (SqlConnection connection1 = new
SqlConnection(connectString1))
{
// Opening connection1 automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Do work in the first connection.
// Assumes conditional logic in place where the second
// connection will only be opened as needed.
using (SqlConnection connection2 = new
SqlConnection(connectString2))
{
// Open the second connection, which enlists the
// second connection and promotes the transaction to
// a full distributed transaction.
connection2.Open();
// Do work in the second connection.
}
}
// The Complete method commits the transaction.
transScope.Complete();
}