
Promotable Transactions in SQL Server 2008
In version 2.0 of the .NET Framework and SQL Server 2005, opening a second connection inside a TransactionScope would automatically promote the transaction to a full distributed transaction, even if both connections were using identical connection strings. In this case, a distributed transaction adds unnecessary overhead that decreases performance.
Starting with SQL Server 2008 and version 3.5 of the .NET Framework, local transactions are no longer promoted to distributed transactions if another connection is opened in the transaction after the previous transaction is closed. This requires no changes to your code if you are already using connection pooling and enlisting in transactions. The following sections describe the situation in more detail.
Understanding Lightweight Transactions
A lightweight transaction is an agreement between SqlClient and the transaction, where SqlClient manages transacting the work on the transaction’s behalf. SqlClient starts a local server transaction when enlisting into the transaction, and the transaction sends a request to roll back or commit the work at the end.
When a second resource tries to enlist in the transaction using the same connection, the local transaction manager does not detect that there is already a connection enlisted to the same data source. In this situation, the local transaction promotes to a full distributed transaction.
Even though both connections are connecting to the same data source, connection pooling will not enable you to open another connection and enlist in the same transaction.
Understanding Connection Pooling and Transactions
A SqlConnection consists of two parts: the public instance that your code interacts with (the outer connection) and a hidden connection that represents an actual server connection (the inner connection).
When you call the Open method on the outer connection, the connection manager looks for a free inner connection from the pool that is associated with the transaction. It will create a new inner connection if one does not exist. When the Close method is called on the outer connection, it returns the inner connection to the pool, where the connection State is reset to ensure a clean slate when it is reused from the pool. SQL Server does not distinguish between the outer and inner connections, so only one outer connection can be used with one inner connection at a time.
When a pooled connection is reset, cursors are closed, options are set back to defaults, the database context is switched back to the one specified in the connection string, and all outstanding local transactions are rolled back. This behavior provides a known starting state every time that you open a connection from the pool. It also prevents you from reusing the inner connection until the local transaction finishes. When you close the transaction's outer connection, the inner connection is set aside pending a commit or rollback request from the transaction manager. When you open a new outer connection while the inner connection is still active, the pool will be empty, and a new inner connection is created. The local transaction manager will then detect that two different resources are trying to enlist in the same transaction. Because SQL Server connections cannot share in local transactions, the transaction manager must promote the transaction to a full distributed transaction.
Extensions Added for SQL Server 2008
SQL Server 2008 adds a new connection reset mode that does not roll back local transactions. This enables SqlClient to return the inner connection to the pool to be reused, even when the connection is associated with a lightweight transaction. A second connection to the same data source can be associated with the existing local transaction without the transaction manager detecting a second transaction.
However, if you try to open a second outer transaction before closing the first connection, there will not be a free connection in the pool. A second inner connection will then be enlisted, and the transaction will be promoted. If connection pooling is turned off, or if a second connection uses a slightly different connection string, the inner connection will be unavailable even if it is sitting idle in the pool. For more information about SQL Server connection pooling, see SQL Server Connection Pooling (ADO.NET).
No changes to your code are necessary in order to take advantage of this new functionality in SQL Server 2008. However, if you are enlisting in SQL Server 2005 transactions, pooling behavior reverts back to version 2.0 of the .NET Framework.