291

私たちのプロジェクトでは、TransactionScope を使用して、データ アクセス レイヤーがトランザクション内でそのアクションを確実に実行するようにしています。エンド ユーザーのマシンで MSDTC サービスを有効にする必要がないことを目指しています。

問題は、半分の開発者マシンで、MSDTC を無効にして実行できることです。残りの半分は有効にする必要があります。そうしないと、"[SERVER] の MSDTC は利用できません" というエラー メッセージが表示されます。

本当に頭を悩ませており、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();
}

私たちは本当に掘り下げて、これを理解しようとしました。これが動作するマシンに関する情報は次のとおりです。

  • 開発 1: Windows 7 x64 SQL2008
  • 開発 2: Windows 7 x86 SQL2008
  • 開発 3: Windows 7 x64 SQL2005 SQL2008

動作しない開発者:

  • 開発 4: Windows 7 x64、SQL2008 SQL2005
  • 開発 5: Windows Vista x86、SQL2005
  • 開発 6: Windows XP X86、SQL2005
  • 自宅の PC : Windows Vista Home Premium、x86、SQL2005

問題を突き止めようとして、Microsoft Update から入手できるすべてのパッチがすべてのマシンに適用されていることを付け加えておきます。

更新 1:

MSDN のトランザクション エスカレーション ページには、次の条件によりトランザクションが DTC にエスカレートされると記載されています。

  1. 単一フェーズの通知をサポートしていない少なくとも 1 つの永続的なリソースが、トランザクションに登録されています。
  2. 単一フェーズの通知をサポートする少なくとも 2 つの永続的なリソースが、トランザクションに登録されます。たとえば、単一の接続を登録しても、トランザクションは昇格されません。ただし、データベースへの 2 番目の接続を開いてデータベースを参加させると、System.Transactions インフラストラクチャは、それがトランザクション内の 2 番目の永続的なリソースであることを検出し、MSDTC トランザクションにエスカレートします。
  3. トランザクションを別のアプリケーション ドメインまたは別のプロセスに「マーシャリング」する要求が呼び出されます。たとえば、アプリケーション ドメインの境界を越えたトランザクション オブジェクトのシリアル化です。トランザクション オブジェクトは値によってマーシャリングされます。つまり、アプリケーション ドメインの境界を越えて渡そうとすると (同じプロセスであっても)、トランザクション オブジェクトがシリアル化されます。Transaction をパラメーターとして受け取るリモート メソッドで呼び出しを行うことによって、トランザクション オブジェクトを渡すことができます。または、リモート トランザクション サービス コンポーネントへのアクセスを試みることができます。これにより、トランザクション オブジェクトがシリアル化され、アプリケーション ドメイン全体でトランザクションがシリアル化される場合と同様に、エスカレーションが発生します。これは分散されており、ローカル トランザクション マネージャーはもはや適切ではありません。

私たちは#3を経験していません。#2は、一度に1つの接続しかなく、単一の「永続的なリソース」に対しても接続されるため、発生していません。#1が起こる方法はありますか?単一フェーズ通知をサポートしない原因となる SQL2005/8 構成はありますか?

更新 2:

個人的に、全員の SQL Server バージョンを再調査しました。「Dev 3」には実際には SQL2008 があり、「Dev 4」には実際には SQL2005 があります。同僚を二度と信用しないように教えてくれます。;) このデータの変更により、問題が見つかったと確信しています。SQL2008 には、SQL2005 にはない大量の素晴らしい機能が含まれているため、SQL2008 の開発者はこの問題に直面していませんでした。

また、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を単一の 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 がインスタンス化されてから不要になるまで、何らかのグローバル接続オブジェクトを開いたままにしておく必要があります。グローバル接続オブジェクトのコード臭に加えて、最初に接続を開いて最後に閉じることは、接続をできるだけ遅く開き、できるだけ早く閉じるというロジックに反します。

4

7 に答える 7

61

このトピックに関する私の研究の結果:

ここに画像の説明を入力

分散トランザクションへの不要なエスカレーションの回避を参照してください

私はまだ Oracle のエスカレーション動作を調査しています: 同じ DB への複数の接続にまたがるトランザクションは DTC にエスカレートしますか?

于 2011-03-23T07:15:56.147 に答える
33

そのコード、2005に接続するときにエスカレーションを引き起こします。

MSDNのドキュメントを確認してください-http://msdn.microsoft.com/en-us/library/ms172070.aspx

SQLServer2008でプロモート可能なトランザクション

.NETFrameworkおよびSQLServer2005のバージョン2.0では、TransactionScope内で2番目の接続を開くと、両方の接続が同じ接続文字列を使用している場合でも、トランザクションが完全分散トランザクションに自動的にプロモートされます。この場合、分散トランザクションは不要なオーバーヘッドを追加し、パフォーマンスを低下させます。

SQL Server 2008およびバージョン3.5の.NETFramework以降、前のトランザクションが閉じられた後にトランザクションで別の接続が開かれた場合、ローカルトランザクションは分散トランザクションに昇格されなくなりました。すでに接続プールとトランザクションへの参加を使用している場合は、コードを変更する必要はありません。

Dev 3:Windows 7 x64、SQL2005が成功し、Dev 4:Windows7x64が失敗する理由を説明できません。それが逆ではないのですか?

于 2009-11-07T17:33:54.620 に答える
13

この回答が削除された理由はわかりませんが、これにはいくつかの関連情報があるようです。

10 年 8 月 4 日 17:42 にエドゥアルドが回答

  1. 接続文字列でEnlist=falseを設定して、トランザクションでの自動登録を回避します。

  2. 接続をトランザクション スコープの参加者として手動で登録します。[元の記事は古い] またはこれを行う:自動 MSDTC 昇格を防ぐ方法 [archive.is]

于 2013-10-09T13:31:19.983 に答える
2

ネストされた接続が問題であるかどうかはよくわかりません。SQL サーバーのローカル インスタンスを呼び出していますが、DTC が生成されませんか??

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }
于 2012-05-13T15:56:56.230 に答える
1

内部で複数の接続へのアクセスを使用する場合、TransactionScopeは常にDTCトランザクションにエスカレートします。上記のコードがDTCを無効にして機能する唯一の方法は、万が一、接続プールから両方の時間に同じ接続を取得する場合です。

「問題は、開発者のマシンの半分で、MSDTCを無効にして実行できることです。」無効になっていることを確認します;)

于 2009-11-07T00:28:12.500 に答える