4

2つのテーブルを持つSQL Server 2008データベースを持っています。TransactionScopeを使用する.NET 4.5アプリケーションと、データベースへの2つのクエリa シングル接続文字列。トランザクションスコープ外で実行されたクエリで分散するようにMSDTCトランザクションを分散する

正常に動作します。このコード:私はテストロールバックにスコープ内で例外をスローする場合のものは奇妙な取得するとき

internal static void Main(string[] args) 
{ 
    string connStr = string.Format(
     "Data Source={0};Initial Catalog={1};" + 
     "Integrated Security=True", "My DB Server", "My Database"); 

    using (var scope = new TransactionScope()) 
    { 
     string query1 = "(actual query not relevant)"; 
     var ds = GetSqlServerDataSet(query1, connStr); 
     string query2 = "(actual query not relevant)"; 
     var ds1 = GetSqlServerDataSet(query2, connStr); 
     scope.Complete(); 
    } 
} 

private static System.Data.DataSet GetSqlServerDataSet(string usingQuery, 
    string usingConnectionString, 
    params System.Data.SqlClient.SqlParameter[] withParameters) 
{ 
    using (var ds = new System.Data.DataSet()) 
    using (var conn = new System.Data.SqlClient.SqlConnection(usingConnectionString)) 
    using (var command = new System.Data.SqlClient.SqlCommand(usingQuery, conn)) 
    { 
     command.Parameters.AddRange(withParameters); 

     using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command)) 
     { 
      adapter.Fill(ds); 
     } 

     return ds; 
    } 
} 

は今、それはです。私は、データベースに関する例外に関する情報を保持するために書いた、参照されたクラスライブラリを使用しています。同じタイプのセットアップ - 同じSQL Server、.NETバージョン、同一のADO.NETコードなどです。これは別のデータベースに書き込むだけです。

しくみはこうだ:私は私のアプリにこのメソッドを追加しました:

private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e) 
{ 
    ExceptionHandling.Core.Main.ProcessException((Exception) e.ExceptionObject); 
    Environment.Exit(0); 
} 

と私は私のMainメソッドの先頭に次の行を追加しました:今

AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; 

私は例外を投げる、例えばthrow new Exception("blah"); の直前でscope.Complete()の前に自動的にHandleUnhandledExceptionにジャンプし、残りは上記のように発生します。これは、メッセージにSystem.Transactions.TransactionManagerCommunicationExceptionになり:分散トランザクションマネージャ(MSDTC)のための

ネットワークアクセスは 無効になっています。コンポーネントサービスの管理 ツールを使用して、MSDTCのセキュリティ設定 のネットワークアクセスでDTCを有効にしてください。

これは、私のクラスライブラリのConnection.Open()への呼び出しで発生します。

ここで何が起こっていますか?私はエラーが私に言っていることを知っている、と私はhow to fix itを知っている。私の質問はです。なぜこれが最初に起こっていますか?私はHandleUnhandledExceptionメソッドに当たる前にusingステートメントがトランザクションをロールバックして処理すると考えていたので、ここでは2つのトランザクションが含まれるべきではありません。または私は間違っていますか?

+0

参照されているクラスライブラリは、MSDTCを単独で必要としないことは確かですか? –

+0

絶対に。私は10年間私のプロジェクトでそれを使ってきました。 –

+0

どのようなアプリですか?コンソール、WPF、Webb? –

答えて

4

あなたのHandleUnhandledExceptionメソッドが実行されると、あなたはまだusingブロック内にあり、finallyの部分がまだ実行されていないために発生します。それは、このコードを検証するのは簡単です:

class Program { 
    static void Main(string[] args) { 
     AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; 
     try { 
      throw new Exception("test"); 
     } 
     finally { 
      Console.WriteLine("finally"); 
     } 
    } 

    private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { 
     Console.WriteLine("handle exception"); 
    } 
} 

出力する:

handle exception 
Unhandled exception: System.Exception: test ... 
finally 

あなたは、関連する行動の議論のために(例えばエリック・リッパーからとコメント、)this質問を読むことをお勧めします。しかし、私は理由はあなたの質問にはあまり関係しないと思う - それはちょうどこのように働くと言うことができる。

あなたの例外を守る理由は明らかです。あなたはコミットされていないトランザクションスコープ内にいて、別のデータベースへの接続を試みています。

あなたのケースに頭に浮かぶ一つの解決策は、(あなたがとにかくあなたのロギングコードは、どのような方法で周囲のトランザクションに影響を与えたくないので)私たちは内部取引されているかどうかを確認し、そうならば、それを抑制することである。

private static void HandleException(Exception ex) { 
    TransactionScope scope = null; 
    if (Transaction.Current != null) 
     scope = new TransactionScope(TransactionScopeOption.Suppress); 
    try { 
     // do stuff 
    } 
    finally { 
     scope?.Dispose(); 
    } 
} 

または常にちょうどsupressed TransactionScopeの中で実行してください。

+0

ブリリアント!どうもありがとう。 –

+0

@ rory.ap言いたいことをもう一つ忘れてしまった。あなたが例外ハンドラで 'Environment.Exit'を使うのを見ました。これで、最終的にブロックがまったく実行されません(私の答えと同じ例で見やすい)。この場合、どれくらい危険なのかは分かりませんが、私が気づいているものはあります。また、コントロールはデフォルトの未処理例外ハンドラに到達しないため、プロセスはクラッシュしません。 – Evk

関連する問題