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();
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
}
}
このメッセージは、トランザクションがSystem.Transactions
のmaxTimeout
よりも長い期間実行されている場合に発生することがあります。 TransactionOptions.Timeout
が増加しても、maxTimeout
を超えることはできません。
maxTimeout
のデフォルト値は10分に設定されており、その値はmachine.config
でonlyのみ変更できます
タイムアウトを変更するには、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
問題を再現できます。トランザクションタイムアウトです。
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ステートメントを実行する前に、トランザクションを破棄する必要があります。
この問題を解決するには、クエリの実行速度を上げるか、タイムアウトを増やします。
TransactionScope
内で例外が発生した場合、ロールバックされます。これは、TransactionScope
が完了したことを意味します。ここでdispose()
を呼び出して、新しいトランザクションを開始する必要があります。古いTransactionScope
を再利用できるかどうかは正直わかりません。試したことはありませんが、そうではないと思います。
私の問題は馬鹿げたものでした。もしあなたがタイムアウトの間にデバッグブレークに座るなら、あなたはこれを得るでしょう。 フェイスパーム
男、プログラミングはいつかあなたを厚く感じさせます...
このエラーは、トランザクションタイムアウトによっても発生することが確認されています。マーカス+ロルフが述べたことに追加するために、TransactionScope
に明示的にタイムアウトを設定していない場合、タイムアウトTimeSpanはデフォルト値を想定します。このデフォルト値はsmaller of:
ローカルのapp.config
/web.config
設定をオーバーライドした場合、例:.
<system.transactions>
<defaultSettings timeout="00:05:00" />
</system.transactions>
ただし、これはmachine.config
設定<machineSettings maxTimeout="00:10:00" />
で「制限」されます
この例外は、disableMicrosoft Distributed Transaction Coordinator
によっても発生します。
有効にする場合は、「dcomcnfg」を実行し、"Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"
を選択して「Properties」を選択します。
「Allow Remote Client」、「Allow Inbound」、「Allow Outbound」および「認証不要」。