このコードではTransactionScopeを使用しています。
private void ExecuteSP()
{
bool IsComplete = false;
SqlCommand sqlComm = null;
//6 hours!!!
TimeSpan ts1 = new TimeSpan(6, 0, 0);
try
{
using (TransactionScope t = new TransactionScope(TransactionScopeOption.RequiresNew, ts1))
{
using (SqlConnection sqlConn = new SqlConnection(GetConnectionString()))
{
//open sql connection
sqlConn.Open();
try
{
//create new sqlCommand
sqlComm = new SqlCommand();
for (int i = 1; i <= 2; i++)
{
IsComplete = true;
//This command takes 15 minutes
sqlComm.CommandText = "exec TestSp";
sqlComm.Connection = sqlConn;
sqlComm.CommandType = CommandType.Text;
sqlComm.CommandTimeout = 18000;
//Executing my command
int j = sqlComm.ExecuteNonQuery();
}
//End
t.Complete();
}
catch (Exception ex)
{
IsComplete = false;
string Message = ex.Message;
}
finally
{
if (sqlComm != null)
sqlComm.Dispose();
}
}
}
}
catch (Exception ex)
{
string messagee = ex.Message;
//do something
}
finally
{
MessageBox.Show("Finsh");
}
}
これは、実行に10分以上かかる1回の実行(sqlCommand.ExecuteNonQuery();)の後に発生します。次の例外でこの例外が発生するまで、この時点では例外は発生しません。
The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.
これは、System.Transactions.TransactionManager.MaximumTimeoutが10分のTimeSpanに設定されているためです。
検索したところ、「System.Transactions-> machine.config's maxTimeout」に関連している可能性がありますが、構成をこのファイルに変更すると例外が発生します。
<?xml version="1.0"?>
<configuration>
<system.transactions>
<machineSettings maxTimeout="10:00:00"/>
</system.transactions>
<appSettings>
<add key="FileName" value="MyFileName" />
<add key="MySpace" value="5 MB" />
<add key="ClientSettingsProvider.ServiceUri" value="" />
</appSettings>
</configuration>
構成ファイルを変更した後、実行時にSystem.Transactions.TransactionManager.MaximumTimeoutを取得しようとすると、次の例外が発生します。
「構成システムの初期化に失敗しました」
誰かがこの問題を解決する方法の手がかりを持っていますか?
(私の場合の一般的な注意:SQLでintを含むテーブルをbigintに変換する必要があるため(int = 32ビット、bigint = 64ビット)、約20分かかるストアドプロシージャを実行する必要があります)新しいテーブルを作成して挿入する必要があります古いテーブルからint64を使用した新しいテーブルへのデータ。IDによって他の4つのテーブルに接続されたテーブルには、それぞれが2,000万行を超え、バインディング、インデックス作成なども含まれています。このプロシージャを小さなストアドプロシージャに分割することはできません。最大タイムアウトを1時間以上に変更する必要があります。10分では不十分です!)。
リフレクションを使用することを恐れていない場合は、プログラムで最大タイムアウトを実際にオーバーライドできます。このコードは将来性があることが保証されていませんが、.NET4.0以降で機能します。
public static class TransactionmanagerHelper
{
public static void OverrideMaximumTimeout(TimeSpan timeout)
{
//TransactionScope inherits a *maximum* timeout from Machine.config. There's no way to override it from
//code unless you use reflection. Hence this code!
//TransactionManager._cachedMaxTimeout
var type = typeof(TransactionManager);
var cachedMaxTimeout = type.GetField("_cachedMaxTimeout", BindingFlags.NonPublic | BindingFlags.Static);
cachedMaxTimeout.SetValue(null, true);
//TransactionManager._maximumTimeout
var maximumTimeout = type.GetField("_maximumTimeout", BindingFlags.NonPublic | BindingFlags.Static);
maximumTimeout.SetValue(null, timeout);
}
}
次のように使用できます。
TransactionmanagerHelper.OverrideMaximumTimeout(TimeSpan.FromMinutes(30));
独自の構成ファイルでmachineSettings
を指定することはできませんが、コンピューターのmachineSettings\maxTimeout
ファイルでmachine.config
を実際に変更/追加する必要があります。
コンピューター上の32/64ビットとCLRバージョンの組み合わせごとに、このファイルのインスタンスが1つあります。たとえば、.NET 2.0用の32ビットバージョンのファイルは、%windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG
ディレクトリにあります。 64ビットの.NET2.0アプリケーションのファイルは、%windir%\Microsoft.NET\Framework64\v2.0.50727\CONFIG
ディレクトリにあります。同様に、.NET 4.0を使用している場合は、v4.0.30319\Config
サブディレクトリ内のファイルを変更する必要があります。
このファイルを変更すると(名前が示すように)、ボックスのすべてのトランザクションの最大タイムアウトが変更されることに注意してください。したがって、ここで設定する内容に注意してください。あなたの例では、タイムアウトを10(!)時間に変更します。
タイムアウトに達するまでデッドロックを検出できない場合があるため、トランザクションのタイムアウトが長すぎると問題になる可能性があります。そのため、このような状況をタイムリーに検出できない場合があります。長時間実行されるトランザクションを回避する必要がある理由は他にもありますが、これは実際にはこの質問/回答の範囲外です。
とにかく、個々のアプリケーションは独自の最大タイムアウトを設定できますが、独自の構成ファイルのmachine.config
セクションを使用して、常にsystem.transactions
ファイルのタイムアウトによって制限されます。
<system.transactions>
<defaultSettings timeout="22:00:00"/>
</system.transactions>
ここでの要素名はdefaultSettings
ではなくmachineSettings
であることに注意してください。
詳細については、次のリンクを確認することもできます。
MSが提供する例とは異なるコードから私が気づいたことの1つは、SQL作業を完了する前にトランザクションを完了することです。
例えば、
t.Complete();
前に来る
if (sqlComm != null)
sqlComm.Dispose();
あなたのt.complete();本当にあなたのSQLセグメントの終わりに移動する必要があります。これを実行して機能することを証明したことはありませんが、トランザクションセクションがあり、完了したことを伝えた後、さらに作業を行うことは理にかなっています。
私はこれを参照として使用しました: http://msdn.Microsoft.com/en-us/library/system.transactions.transactionscope.aspx
次のように、構成ファイル内の要素の順序を入れ替えて、appSettings
がsystem.transactions
の前に来るようにしてください。
<appSettings>
...
</appSettings>
<system.transactions>
...
</system.transactions>
また、そこにconfigSections
がある場合は、同様に行います。
デフォルト値は次のとおりです。
Transaction Binding=Implicit Unbind.
Implicit Unbindを使用すると、接続が終了したときにトランザクションから切り離されます。まれに、人々は以下を使用します。
Binding=Explicit Unbind
あなたのケースでこれをチェックすることができます。