1. ホーム
  2. c#

[解決済み】一部のマシンでTransactionScopeが自動的にMSDTCにエスカレートする?

2022-04-16 11:14:02

質問

私たちのプロジェクトでは、データアクセス層がトランザクション内でアクションを実行することを保証するためにTransactionScopeを使用しています。私たちの目標は ではなく は、エンドユーザーのマシンで MSDTC サービスが有効になっている必要があります。

問題は、開発者の半分のマシンでは、MSDTCを無効にしたまま実行できることです。 残りの半分はMSDTCを有効にしなければなりません。 MSDTC on [SERVER] is unavailable" のエラーメッセージが表示されます。

ADO.NETのトランザクションオブジェクトをベースにしたTransactionScopeのような自作のソリューションに戻そうかと真剣に悩んでいます。開発者の半数で動作している(そしてエスカレートしていない)同じコードで、そのようなことができるなんて、正気の沙汰とは思えません。 もう一人の開発者のものではエスカレートしてしまうのです。

に良い回答があればと思いました。 トランザクションがDTCにエスカレートされた理由を追跡する が、残念ながらない。

以下はトラブルの原因となるコードのサンプルで、エスカレーションしようとするマシンでは、2番目のconnection.Open()でエスカレーションしようとします(そう、その時は他の接続は開いていないのです)。

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

私たちは本当に掘り下げて、これを解明しようとしました。 以下、動作するマシンについての情報です。

  • Dev 1: Windows 7 x64 SQL2008
  • Dev 2: Windows 7 x86 SQL2008
  • 開発環境3:Windows 7 x64 <ストライク SQL2005 SQL2008

動作しないデベロッパー

  • Dev 4: Windows 7 x64, <ストライク SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • 開発環境6:Windows XP X86、SQL2005
  • 自宅PC:Windows Vista Home Premium, x86, SQL2005

なお、問題を突き止めるために、すべてのマシンにマイクロソフト・アップデートで提供されているすべてのパッチを適用しています。

アップデート1:

そのMSDNのtransaction-escalationのページには、以下の条件でトランザクションがDTCにエスカレーションされると書かれています。

  1. 単相通知をサポートしない耐久性のあるリソースが少なくとも1つ、トランザクションに登録されていること。
  2. 単相通知をサポートする少なくとも2つの耐久性のあるリソースがトランザクションに登録されています。例えば、1 つの接続を参加させても、トランザクションは昇格しません。ただし、データベースへの 2 つ目の接続を開いてデータベースが参加するたびに、System.Transactions インフラストラクチャは、それがトランザクション内の 2 つ目の耐久性のあるリソースであることを検出し、それを MSDTC トランザクションにエスカレートさせます。
  3. 異なるアプリケーションドメインまたは異なるプロセスへのトランザクションをマーシャルする要求が呼び出される。例えば、アプリケーションドメインの境界を越えたトランザクションオブジェクトのシリアライズ。トランザクション・オブジェクトはmarshaled-by-valueであり、アプリケーション・ドメインの境界を越えて渡そうとすると(同じプロセスであっても)、トランザクション・オブジェクトのシリアライズが行われることを意味する。トランザクションオブジェクトを渡すには、Transactionをパラメータとして受け取るリモートメソッド上で呼び出しを行うか、リモートトランザクションサービスコンポーネントにアクセスしようとすることができる。これはトランザクションオブジェクトをシリアライズし、アプリケーションドメイン全体でトランザクションがシリアライズされるときのようなエスカレーションをもたらす。分散され、ローカルのトランザクション・マネージャはもはや適切ではありません。

3.は発生していません。#なぜなら、一度に接続されるのは1つだけであり、それも単一の「耐久性のあるリソース」に対してだからです。 1が起こる可能性はありますか? SQL2005/8の設定により、単相通知がサポートされていないのでしょうか?

更新2:

個人的に全員のSQL Serverのバージョンを再調査したところ、"Dev 3"は実はSQL2008で、"Dev 4"は実はSQL2005だったのだそうです。 これでもう二度と同僚を信用しないようになったよ(笑)。 このデータの変化から、私たちは問題を発見したと確信しています。 SQL2008の開発者は、SQL2005にはない素晴らしい機能が大量に含まれているため、この問題を経験していないのです。

また、SQL2005をサポートすることになったので、今までのようにTransactionScopeを使うことができず、TransactionScopeを使いたければ、単一のSqlConnectionオブジェクトを受け渡す必要があるということです... SqlConnectionを簡単に受け渡すことができない状況では問題があると思われます... グローバルSqlConnectionインスタンスの臭いがします。 ピュー!

アップデート3

ここまでの質問ではっきりさせたいのですが

SQL2008です。

  • 1つのTransactionScope内で複数の接続を可能にする(上記サンプルコードで実証済み)。
  • 注意点1:複数のSqlConnectionsがネストされている場合、つまり2つ以上のSqlConnectionsが同時に開かれた場合、TransactionScopeは直ちにDTCにエスカレートします。
  • 注意点2:別のSqlConnectionが、異なる '耐久性のあるリソース' (例: 別の SQL Server) は直ちに DTC にエスカレートします。

SQL2005です。

  • 1 つの TransactionScope 内で複数の接続を許可しない。 2つ目のSqlConnectionが開かれた場合、エスカレートします。

更新情報4

この問題をより深く理解するために <ストライク めちゃくちゃ SQL2005でDTCにエスカレートさせる方法を説明します。 シングル SqlConnection :

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

これは私には壊れているとしか思えませんが、もしすべての SqlConnection.Open() は、接続プールから取得します。

"なぜこのようなことが起こるのでしょうか。 接続を開く前に SqlTableAdapter を使用すると、SqlTableAdapter は接続を開いて閉じ、トランザクションを効果的に終了します。

つまり、基本的に SQL2005 で TransactionScope をうまく使うには、最初の TransactionScope がインスタンス化されてから不要になるまで開いたままの、ある種のグローバル接続オブジェクトが必要なのです。 グローバル接続オブジェクトのコード臭さに加え、接続を最初に開いて最後に閉じるというのは、接続をできるだけ遅く開き、できるだけ早く閉じるというロジックと相反するものです。

解決方法は?

SQL Server 2008 では、複数の SQLConnection を1つの TransactionScope この場合、複数の物理TCP接続が発生し、エスカレーションが必要になります。

開発者の中には、SQL Server 2005を使用している人と、SQL Server 2008を使用している人がいるようですね。どれがエスカレートしてどれがエスカレートしないか、正しく認識できていますか?

最も明白な説明は、SQL Server 2008を使用している開発者が、エスカレーションしていない開発者であるということでしょう。