web-dev-qa-db-ja.com

TransactionScopeが途中で完了しました

TransactionScope内で実行されるコードブロックがあり、このコードブロック内でDBをいくつか呼び出します。色域全体を選択、更新、作成、削除します。削除を実行するときは、SqlCommandの拡張メソッドを使用して実行します。このメソッドは、クエリがデッドロックにヒットする可能性があるため、デッドロックが発生すると自動的にクエリを再送信します。

デッドロックが発生し、関数がクエリを再送信しようとすると、問題が発生すると思います。これは私が受け取るエラーです:

現在の接続に関連付けられているトランザクションは完了していますが、破棄されていません。接続を使用してSQLステートメントを実行する前に、トランザクションを破棄する必要があります。

これは、クエリを実行する単純なコードです(以下のコードはすべて、TransactionScopeを使用して実行されます)。

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
    sqlCommand.Connection.Open();
    sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}

デッドロッククエリを再送信する拡張メソッドは次のとおりです。

public static class SqlCommandExtender
{
    private const int DEADLOCK_ERROR = 1205;
    private const int MAXIMUM_DEADLOCK_RETRIES = 5;
    private const int SLEEP_INCREMENT = 100;

    public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand)
    {
        int count = 0;
        SqlException deadlockException = null;

        do
        {
            if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT);
            deadlockException = ExecuteNonQuery(sqlCommand);
            count++;
        }
        while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES);

        if (deadlockException != null) throw deadlockException;
    }

    private static SqlException ExecuteNonQuery(SqlCommand sqlCommand)
    {
        try
        {
            sqlCommand.ExecuteNonQuery();
        }
        catch (SqlException exception)
        {
            if (exception.Number == DEADLOCK_ERROR) return exception;
            throw;
        }

        return null;
    }
}

エラーは次の行で発生します。

sqlCommand.ExecuteNonQuery();
64
Chris

TransactionScopeから選択ステートメントを抑制することを忘れないでください。 SQL Server 2005以降では、with(nolock)を使用しても、selectがタッチするテーブルにはロックが作成されます。これを確認してください TransactionScopeのセットアップと使用方法 が表示されます。

using(TransactionScope ts = new TransactionScope 
{ 
  // db calls here are in the transaction 
  using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) 
  { 
    // all db calls here are now not in the transaction 
  } 
} 
60
Dan

このメッセージは、トランザクションがSystem.TransactionsmaxTimeoutよりも長い期間実行されている場合に発生することがあります。 TransactionOptions.Timeoutが増加しても、maxTimeoutを超えることはできません。

maxTimeoutのデフォルト値は10分に設定されており、その値はmachine.configonlyのみ変更できます

タイムアウトを変更するには、machine.configに(構成レベルで)以下を追加します。

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:30:00" />
    </system.transactions>
</configuration>

Machine.configは次の場所にあります:%windir%\Microsoft.NET\Framework\[version]\config\machine.config

詳細については、次のブログ記事をご覧ください: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

41
Marcus

問題を再現できます。トランザクションタイムアウトです。

using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1)))
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var sqlCommand = connection.CreateCommand())
        {
            for (int i = 0; i < 10000; i++)
            {
                sqlCommand.CommandText = "select * from actor";
                using (var sqlDataReader = sqlCommand.ExecuteReader())
                {
                    while (sqlDataReader.Read())
                    {
                    }
                }
            }
        }
    }
}

スローSystem.InvalidOperationExceptionこのメッセージ:

現在の接続に関連付けられているトランザクションは完了していますが、破棄されていません。接続を使用してSQLステートメントを実行する前に、トランザクションを破棄する必要があります。

この問題を解決するには、クエリの実行速度を上げるか、タイムアウトを増やします。

21
Rolf

TransactionScope内で例外が発生した場合、ロールバックされます。これは、TransactionScopeが完了したことを意味します。ここでdispose()を呼び出して、新しいトランザクションを開始する必要があります。古いTransactionScopeを再利用できるかどうかは正直わかりません。試したことはありませんが、そうではないと思います。

11
Donnie

私の問題は馬鹿げたものでした。もしあなたがタイムアウトの間にデバッグブレークに座るなら、あなたはこれを得るでしょう。 フェイスパーム

男、プログラミングはいつかあなたを厚く感じさせます...

6
Samuel Fleming

このエラーは、トランザクションタイムアウトによっても発生することが確認されています。マーカス+ロルフが述べたことに追加するために、TransactionScopeに明示的にタイムアウトを設定していない場合、タイムアウトTimeSpanはデフォルト値を想定します。このデフォルト値はsmaller of:

  1. ローカルのapp.config/web.config設定をオーバーライドした場合、例:.

    <system.transactions>
    <defaultSettings timeout="00:05:00" />
    </system.transactions>
    
  2. ただし、これはmachine.config設定<machineSettings maxTimeout="00:10:00" />で「制限」されます

6
StuartLC

この例外は、disableMicrosoft Distributed Transaction Coordinatorによっても発生します。

有効にする場合は、「dcomcnfg」を実行し、"Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"を選択して「Properties」を選択します。

Allow Remote Client」、「Allow Inbound」、「Allow Outbound」および「認証不要」。

1