SQL Server と System.Transactions の統合System.Transactions Integration with SQL Server

.NET Framework バージョン2.0 では、 System.Transactions名前空間を使用してアクセスできるトランザクションフレームワークが導入されました。The .NET Framework version 2.0 introduced a transaction framework that can be accessed through the System.Transactions namespace. このフレームワークは、ADO.NET など、.NET Framework に完全に統合された方法でトランザクションを公開します。This framework exposes transactions in a way that is fully integrated in the .NET Framework, including ADO.NET.

プログラミングの機能強化に加えてSystem.Transactions 、ADO.NET を連携させることで、トランザクションを操作するときに最適化を調整できます。In addition to the programmability enhancements, System.Transactions and ADO.NET can work together to coordinate optimizations when you work with transactions. 昇格可能なトランザクションとは、必要に応じて完全な分散トランザクションに自動的に昇格する、軽量の (ローカル) トランザクションです。A promotable transaction is a lightweight (local) transaction that can be automatically promoted to a fully distributed transaction on an as-needed basis.

ADO.NET 2.0 以降ではSystem.Data.SqlClient 、SQL Server を操作するときに、昇格可能なトランザクションをサポートしています。Starting with ADO.NET 2.0, System.Data.SqlClient supports promotable transactions when you work with SQL Server. 昇格可能なトランザクションは、必要な場合以外、分散トランザクションのオーバーヘッドの増加を引き起こすことはありません。A promotable transaction does not invoke the added overhead of a distributed transaction unless the added overhead is required. 昇格可能なトランザクションは自動的に行われ、開発者の介入は必要ありません。Promotable transactions are automatic and require no intervention from the developer.

昇格可能なトランザクションは、SQL Server で SQL Server (SqlClient) の .NET Framework Data Provider を使用する場合にのみ使用できます。Promotable transactions are only available when you use the .NET Framework Data Provider for SQL Server (SqlClient) with SQL Server.

昇格可能なトランザクションの作成Creating Promotable Transactions

SQL Server の .NET Framework プロバイダーは、昇格可能なトランザクションをサポートします。これは、.NET Framework System.Transactions名前空間のクラスを通じて処理されます。The .NET Framework Provider for SQL Server provides support for promotable transactions, which are handled through the classes in the .NET Framework System.Transactions namespace. 昇格可能なトランザクションでは、必要が生じるまで分散トランザクションの作成を延期することで、分散トランザクションが最適化されます。Promotable transactions optimize distributed transactions by deferring creating a distributed transaction until it is needed. 必要なリソース マネージャーが 1 つだけである場合は、分散トランザクションは発生しません。If only one resource manager is required, no distributed transaction occurs.

注意

部分信頼のシナリオで分散トランザクションに昇格するには、 DistributedTransactionPermission が必要です。In a partially trusted scenario, the DistributedTransactionPermission is required when a transaction is promoted to a distributed transaction.

昇格可能なトランザクションのシナリオPromotable Transaction Scenarios

分散トランザクションは一般的にシステム リソースを大量に消費するため、トランザクションでアクセスされるすべてのリソース マネージャーを統合する、Microsoft Distributed Transaction Coordinator (MS DTC) で管理されます。Distributed transactions typically consume significant system resources, being managed by Microsoft Distributed Transaction Coordinator (MS DTC), which integrates all the resource managers accessed in the transaction. 昇格可能なトランザクションは特殊な形式System.Transactionsのトランザクションであり、効率的に作業を単純な SQL Server トランザクションに委任します。A promotable transaction is a special form of a System.Transactions transaction that effectively delegates the work to a simple SQL Server transaction. System.TransactionsSystem.Data.SqlClient、および SQL Server トランザクションの処理に関連する作業を調整し、必要に応じて完全な分散トランザクションに昇格させます。System.Transactions, System.Data.SqlClient, and SQL Server coordinate the work involved in handling the transaction, promoting it to a full distributed transaction as needed.

昇格可能なトランザクションを使用する利点は、アクティブな TransactionScope トランザクションによって接続が開かれ、その他の接続が開いていない場合に、完全な分散トランザクションによるオーバーヘッドが生じることなく、トランザクションが軽量なトランザクションとしてコミットされることです。The benefit of using promotable transactions is that when a connection is opened by using an active TransactionScope transaction, and no other connections are opened, the transaction commits as a lightweight transaction, instead of incurring the additional overhead of a full distributed transaction.

接続文字列キーワードConnection String Keywords

ConnectionString プロパティではキーワード Enlistがサポートされており、 System.Data.SqlClient によってトランザクションのコンテキストが検出され、分散トランザクションに接続が自動的に参加するかどうかが示されます。The ConnectionString property supports a keyword, Enlist, which indicates whether System.Data.SqlClient will detect transactional contexts and automatically enlist the connection in a distributed transaction. Enlist=trueである場合は、開いているスレッドの現在のトランザクション コンテキストに接続が自動的に参加します。If Enlist=true, the connection is automatically enlisted in the opening thread's current transaction context. Enlist=falseである場合、 SqlClient 接続は分散トランザクションとは対話しません。If Enlist=false, the SqlClient connection does not interact with a distributed transaction. Enlist の既定値は true です。The default value for Enlist is true. Enlist が接続文字列で指定されていない場合、接続が開いたときに分散トランザクションがあればそれに自動的に参加します。If Enlist is not specified in the connection string, the connection is automatically enlisted in a distributed transaction if one is detected when the connection is opened.

参加する Transaction Binding トランザクションと接続の関連付けは、 SqlConnection 接続文字列の System.Transactions キーワードによって制御されます。The Transaction Binding keywords in a SqlConnection connection string control the connection's association with an enlisted System.Transactions transaction. TransactionBindingSqlConnectionStringBuilderプロパティを介して利用することもできます。It is also available through the TransactionBinding property of a SqlConnectionStringBuilder.

次の表で使用可能な値について説明します。The following table describes the possible values.

キーワードKeyword 説明Description
Implicit UnbindImplicit Unbind これが既定値です。The default. 完了時に接続がトランザクションからデタッチされ、自動コミット モードに切り替わります。The connection detaches from the transaction when it ends, switching back to autocommit mode.
Explicit UnbindExplicit Unbind トランザクションが閉じられるまで、接続がトランザクションにアタッチされたままになります。The connection remains attached to the transaction until the transaction is closed. 関連付けられているトランザクションがアクティブでない場合や、 Currentと一致しない場合、接続は失敗します。The connection will fail if the associated transaction is not active or does not match Current.

TransactionScope の使用Using TransactionScope

TransactionScope クラスでは、接続を分散トランザクションに暗黙的に参加させることで、コード ブロックがトランザクションのコード ブロックになります。The TransactionScope class makes a code block transactional by implicitly enlisting connections in a distributed transaction. Complete ブロックの末尾では、終了する前に TransactionScope メソッドを呼び出す必要があります。You must call the Complete method at the end of the TransactionScope block before leaving it. ブロックを終了すると、 Dispose メソッドが呼び出されます。Leaving the block invokes the Dispose method. 例外がスローされてコードがスコープから外れている場合は、トランザクションがアボートされたと見なされます。If an exception has been thrown that causes the code to leave scope, the transaction is considered aborted.

使用中のブロックを終了したときに using オブジェクトで Dispose が呼び出されるように、 TransactionScope ブロックを使用することをお勧めします。We recommend that you use a using block to make sure that Dispose is called on the TransactionScope object when the using block is exited. 保留中のトランザクションのコミットまたはロールバックに失敗すると、 TransactionScope の既定のタイムアウトが 1 分であるため、パフォーマンスが大幅に低下する場合があります。Failure to commit or roll back pending transactions can significantly damage performance because the default time-out for the TransactionScope is one minute. using ステートメントを使用しない場合は、すべての処理を Try ブロックで実行し、 Dispose メソッドを Finally ブロック内で明示的に呼び出す必要があります。If you do not use a using statement, you must perform all work in a Try block and explicitly call the Dispose method in the Finally block.

TransactionScope内で例外が発生した場合、そのトランザクションは矛盾しているとしてマークされ、破棄されます。If an exception occurs in the TransactionScope, the transaction is marked as inconsistent and is abandoned. トランザクションは、 TransactionScope が破棄されるとロールバックされます。It will be rolled back when the TransactionScope is disposed. 例外が発生しない場合は、参加しているトランザクションがコミットされます。If no exception occurs, participating transactions commit.

注意

既定では、 TransactionScope クラスは、 IsolationLevelSerializable であるトランザクションを作成します。The TransactionScope class creates a transaction with a 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.

注意

データベース リソースを大量に消費するため、分散トランザクション内で実行するのは更新、挿入、削除だけにすることをお勧めします。We recommend that you perform only updates, inserts, and deletes within distributed transactions because they consume significant database resources. Select ステートメントを使用すると、データベース リソースが不必要にロックされることがあり、シナリオによっては選択にトランザクションを使用する必要がある場合があります。Select statements may lock database resources unnecessarily, and in some scenarios, you may have to use transactions for selects. 処理済みのその他のリソース マネージャーに関係する場合以外、データベース以外の処理はトランザクションのスコープ外で実行する必要があります。Any non-database work should be done outside the scope of the transaction, unless it involves other transacted resource managers. トランザクションのスコープ内で例外が発生するとトランザクションのコミットが防止されますが、 TransactionScope クラスでは、作成したコードによってトランザクションのスコープ外で行われた変更はロールバックされません。Although an exception in the scope of the transaction prevents the transaction from committing, the TransactionScope class has no provision for rolling back any changes your code has made outside the scope of the transaction itself. トランザクションがロールバックされたときに何らかの動作を行うようにする場合は、 IEnlistmentNotification インターフェイスを独自に実装し、トランザクションに明示的に参加する必要があります。If you have to take some action when the transaction is rolled back, you must write your own implementation of the IEnlistmentNotification interface and explicitly enlist in the transaction.

Example

System.Transactions を使用する場合は、System.Transactions.dll への参照が必要になります。Working with System.Transactions requires that you have a reference to System.Transactions.dll.

次の関数は、 SqlConnection ブロックでラップされている、異なる 2 つの TransactionScope オブジェクトで表された異なる 2 つの SQL Server インスタンスに対する、昇格可能なトランザクションを作成する方法を示しています。The following function demonstrates how to create a promotable transaction against two different SQL Server instances, represented by two different SqlConnection objects, which are wrapped in a TransactionScope block. このコードにより、 TransactionScope ステートメントを持つ using ブロックが作成され、最初の接続が開き、 TransactionScopeに自動的に参加します。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. 2 つ目の接続は、1 つ目の接続のコマンドで例外がスローされなかった場合にのみ、 TransactionScope に参加します。The second connection is enlisted in the TransactionScope only if the command in the first connection does not throw an exception. 2 つ目の接続が開かれると、トランザクションが完全な分散トランザクションに自動的に昇格されます。When the second connection is opened, the transaction is automatically promoted to a full distributed transaction. Complete メソッドが呼び出され、例外がスローされなかった場合にのみ、トランザクションがコミットされます。The Complete method is invoked, which commits the transaction only if no exceptions have been thrown. TransactionScope ブロックの任意の場所で例外がスローされると、 Complete が呼び出されず、 TransactionScope ブロックの最後で using が破棄されたときに分散トランザクションがロールバックされます。If an exception has been thrown at any point in the TransactionScope block, Complete will not be called, and the distributed transaction will roll back when the TransactionScope is disposed at the end of its using block.

// This function takes arguments for the 2 connection strings and commands in order  
// to create a transaction involving two SQL Servers. It returns a value > 0 if the  
// transaction committed, 0 if the transaction rolled back. To test this code, you can   
// connect to two different databases on the same server by altering the connection string,  
// or to another RDBMS such as Oracle by altering the code in the connection2 code block.  
static public int CreateTransactionScope(  
    string connectString1, string connectString2,  
    string commandText1, string commandText2)  
{  
    // Initialize the return value to zero and create a StringWriter to display results.  
    int returnValue = 0;  
    System.IO.StringWriter writer = new System.IO.StringWriter();  
  
    // Create the TransactionScope in which to execute the commands, guaranteeing  
    // that both commands will commit or roll back as a single unit of work.  
    using (TransactionScope scope = new TransactionScope())  
    {  
        using (SqlConnection connection1 = new SqlConnection(connectString1))  
        {  
            try  
            {  
                // Opening the connection automatically enlists it in the   
                // TransactionScope as a lightweight transaction.  
                connection1.Open();  
  
                // Create the SqlCommand object and execute the first command.  
                SqlCommand command1 = new SqlCommand(commandText1, connection1);  
                returnValue = command1.ExecuteNonQuery();  
                writer.WriteLine("Rows to be affected by command1: {0}", returnValue);  
  
                // if you get here, this means that command1 succeeded. By nesting  
                // the using block for connection2 inside that of connection1, you  
                // conserve server and network resources by opening connection2   
                // only when there is a chance that the transaction can commit.     
                using (SqlConnection connection2 = new SqlConnection(connectString2))  
                    try  
                    {  
                        // The transaction is promoted to a full distributed  
                        // transaction when connection2 is opened.  
                        connection2.Open();  
  
                        // Execute the second command in the second database.  
                        returnValue = 0;  
                        SqlCommand command2 = new SqlCommand(commandText2, connection2);  
                        returnValue = command2.ExecuteNonQuery();  
                        writer.WriteLine("Rows to be affected by command2: {0}", returnValue);  
                    }  
                    catch (Exception ex)  
                    {  
                        // Display information that command2 failed.  
                        writer.WriteLine("returnValue for command2: {0}", returnValue);  
                        writer.WriteLine("Exception Message2: {0}", ex.Message);  
                    }  
            }  
            catch (Exception ex)  
            {  
                // Display information that command1 failed.  
                writer.WriteLine("returnValue for command1: {0}", returnValue);  
                writer.WriteLine("Exception Message1: {0}", ex.Message);  
            }  
        }  
  
        // If an exception has been thrown, Complete will not   
        // be called and the transaction is rolled back.  
        scope.Complete();  
    }  
  
    // The returnValue is greater than 0 if the transaction committed.  
    if (returnValue > 0)  
    {  
        writer.WriteLine("Transaction was committed.");  
    }  
    else  
    {  
        // You could write additional business logic here, notify the caller by  
        // throwing a TransactionAbortedException, or log the failure.  
        writer.WriteLine("Transaction rolled back.");  
    }  
  
    // Display messages.  
    Console.WriteLine(writer.ToString());  
  
    return returnValue;  
}  
' This function takes arguments for the 2 connection strings and commands in order  
' to create a transaction involving two SQL Servers. It returns a value > 0 if the  
' transaction committed, 0 if the transaction rolled back. To test this code, you can   
' connect to two different databases on the same server by altering the connection string,  
' or to another RDBMS such as Oracle by altering the code in the connection2 code block.  
Public Function CreateTransactionScope( _  
  ByVal connectString1 As String, ByVal connectString2 As String, _  
  ByVal commandText1 As String, ByVal commandText2 As String) As Integer  
  
    ' Initialize the return value to zero and create a StringWriter to display results.  
    Dim returnValue As Integer = 0  
    Dim writer As System.IO.StringWriter = New System.IO.StringWriter  
  
    ' Create the TransactionScope in which to execute the commands, guaranteeing  
    ' that both commands will commit or roll back as a single unit of work.  
    Using scope As New TransactionScope()  
        Using connection1 As New SqlConnection(connectString1)  
            Try  
                ' Opening the connection automatically enlists it in the   
                ' TransactionScope as a lightweight transaction.  
                connection1.Open()  
  
                ' Create the SqlCommand object and execute the first command.  
                Dim command1 As SqlCommand = New SqlCommand(commandText1, connection1)  
                returnValue = command1.ExecuteNonQuery()  
                writer.WriteLine("Rows to be affected by command1: {0}", returnValue)  
  
                ' If you get here, this means that command1 succeeded. By nesting  
                ' the Using block for connection2 inside that of connection1, you  
                ' conserve server and network resources by opening connection2   
                ' only when there is a chance that the transaction can commit.     
                Using connection2 As New SqlConnection(connectString2)  
                    Try  
                        ' The transaction is promoted to a full distributed  
                        ' transaction when connection2 is opened.  
                        connection2.Open()  
  
                        ' Execute the second command in the second database.  
                        returnValue = 0  
                        Dim command2 As SqlCommand = New SqlCommand(commandText2, connection2)  
                        returnValue = command2.ExecuteNonQuery()  
                        writer.WriteLine("Rows to be affected by command2: {0}", returnValue)  
  
                    Catch ex As Exception  
                        ' Display information that command2 failed.  
                        writer.WriteLine("returnValue for command2: {0}", returnValue)  
                        writer.WriteLine("Exception Message2: {0}", ex.Message)  
                    End Try  
                End Using  
  
            Catch ex As Exception  
                ' Display information that command1 failed.  
                writer.WriteLine("returnValue for command1: {0}", returnValue)  
                writer.WriteLine("Exception Message1: {0}", ex.Message)  
            End Try  
        End Using  
  
        ' If an exception has been thrown, Complete will   
        ' not be called and the transaction is rolled back.  
        scope.Complete()  
    End Using  
  
    ' The returnValue is greater than 0 if the transaction committed.  
    If returnValue > 0 Then  
        writer.WriteLine("Transaction was committed.")  
    Else  
        ' You could write additional business logic here, notify the caller by  
        ' throwing a TransactionAbortedException, or log the failure.  
       writer.WriteLine("Transaction rolled back.")  
     End If  
  
    ' Display messages.  
    Console.WriteLine(writer.ToString())  
  
    Return returnValue  
End Function  

関連項目See also