web-dev-qa-db-ja.com

TransactionScope maximumTimeout

このコードでは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分では不十分です!)。

15
user436862

リフレクションを使用することを恐れていない場合は、プログラムで最大タイムアウトを実際にオーバーライドできます。このコードは将来性があることが保証されていませんが、.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));
30
Matt Honeycutt

独自の構成ファイルで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であることに注意してください。

詳細については、次のリンクを確認することもできます。

12
Christian.K

MSが提供する例とは異なるコードから私が気づいたことの1つは、SQL作業を完了する前にトランザクションを完了することです。

例えば、

 t.Complete(); 

前に来る

            if (sqlComm != null)
                sqlComm.Dispose();  

あなたのt.complete();本当にあなたのSQLセグメントの終わりに移動する必要があります。これを実行して機能することを証明したことはありませんが、トランザクションセクションがあり、完了したことを伝えた後、さらに作業を行うことは理にかなっています。

私はこれを参照として使用しました: http://msdn.Microsoft.com/en-us/library/system.transactions.transactionscope.aspx

0
BugFinder

次のように、構成ファイル内の要素の順序を入れ替えて、appSettingssystem.transactionsの前に来るようにしてください。

<appSettings>    
  ...     
</appSettings> 
<system.transactions>
  ...
</system.transactions> 

また、そこにconfigSectionsがある場合は、同様に行います。

0
Grant Thomas

デフォルト値は次のとおりです。

Transaction Binding=Implicit Unbind. 

Implicit Unbindを使用すると、接続が終了したときにトランザクションから切り離されます。まれに、人々は以下を使用します。

Binding=Explicit Unbind

あなたのケースでこれをチェックすることができます。

0
Abhijit Kumar