一部のマシンではTransactionScopeが自動的にMSDTCにエスカレートしますか?


284

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

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

それは本当に頭を悩ませる原因になり、ADO.NETトランザクションオブジェクトに基づく自作のTransactionScopeのようなソリューションにロールバックすることを真剣に検討するように私に促します。それは一見非常識だ-私たちの開発者の半分の作品は(とエスカレートしない)と同じコード、他の開発者にエスカレートします。

トランザクションがDTCにエスカレートされる理由トレースに適切に回答することを望んでいましたが、残念ながらそうではありません。

問題を引き起こすコードのサンプルは次のとおりです。エスカレートしようとするマシンでは、2番目の接続でエスカレートしようとします。

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. トランザクションを別のアプリケーションドメインまたは別のプロセスに「マーシャリング」する要求が呼び出されます。たとえば、アプリケーションドメインの境界を越えたトランザクションオブジェクトのシリアル化。トランザクションオブジェクトは値でマーシャリングされます。つまり、それを(同じプロセス内であっても)アプリケーションドメインの境界を越えて渡そうとすると、トランザクションオブジェクトがシリアル化されます。トランザクションオブジェクトを渡すには、トランザクションをパラメータとして取るリモートメソッドを呼び出すか、リモートのトランザクションサービスコンポーネントにアクセスしてみます。これにより、トランザクションオブジェクトがシリアル化され、アプリケーションドメイン全体でトランザクションがシリアル化される場合と同様に、エスカレーションが行われます。それは配布されており、ローカルトランザクションマネージャーはもはや適切ではありません。

#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:これらの複数のSqlConnectionがネストされている場合、つまり、2つ以上のSqlConnectionが同時に開かれている場合、TransactionScopeはすぐにDTCにエスカレートします。
  • 警告#2:追加のSqlConnectionが別の「永続リソース」(つまり、別のSQL Server)に対して開かれている場合、すぐにDTCにエスカレートされます

SQL2005:

  • 1つのTransactionScope期間内に複数の接続を許可しません。2番目のSqlConnectionが開かれると、エスカレートします。

アップデート4

さらに、この質問をするの関心では混乱するだけで、より明瞭のために便利な、と、ここにあなたがDTCにエスカレートする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が不要になるまでインスタンス化された時点から開いたままの何らかのグローバル接続オブジェクトが必要です。グローバル接続オブジェクトのコードの臭いに加えて、接続を最初に開いて最後に閉じることは、接続を可能な限り遅く開いて、できるだけ早く閉じるというロジックとは相容れません。


「アンビエントトランザクションに関与するかどうかに関係なく、ここで他のことを行う」を拡張できますか。確かにそこにあるものはコードの動作に大きく影響しますか?
RichardOD 2009年

2
「#2は一度に1つの接続しかないため、発生していません」-#2は、2番目の接続を同時に開く必要があるとは言っていません。同じトランザクションに参加する必要があるだけです。
ジョー

3
単一のSqlConnectionでエスカレーションがどのように発生するかを示すUpdate 4で報告していただきありがとうございます。これは、1つのSqlConnectionのみが使用されることを注意深く確認しているにもかかわらず、まさに私が実行していたものです。クレイジーなのは私ではなくコンピューターだと知ってよかった。:-)
オランデニソン2012

接続プールに関して、複数の接続がある場合(必要に応じてネストされている場合)、一度に1つずつ開閉する場合、1つの実際の会議プールリソースまたは接続ごとに1つを使用しているので、これを合理化して判断します。適切に範囲指定された「参加可能な」接続があるかどうか(これは避けたい)
brumScouse

1
同じトランザクションスコープでネストされた接続は、分散トランザクションに昇格します。SQL Server 2008以降では、同じトランザクションスコープの下の複数の(ネストされていない)接続は、分散トランザクションに昇格しません。
PreguntonCojoneroCabrón

回答:


71

SQL Server 2008は、接続が同時に開いていない場合、エスカレートせずに複数SQLConnectionのを1つに使用できますTransactionScope。これにより、複数の「物理」TCP接続が発生し、エスカレーションが必要になります。

一部の開発者はSQL Server 2005を使用しており、他の開発者はSQL Server 2008を使用しています。エスカレーションしているものとそうでないものを正しく特定しましたか?

最も明白な説明は、SQL Server 2008を使用している開発者はエスカレートしていないことです。


はい、詳細は正しく、実際にコードを見ている人はいますか?トランザクションスコープ内には2つの接続がありますが、一度にインスタンス化されて開かれる接続は1つだけです。また、いいえ、DTCは動作しているマシンで実行されていません。
Yoopergeek 2009年

1
「しかし、一度にインスタンス化されて開かれる接続は1つしかありません」-なぜそれが関連するのですか?SQL2005では、トランザクションスコープ内で複数の接続を開くと、それらが同時に開いたままであるかどうかに関係なくエスカレートします。あなたがそれについて考えるなら、それは論理的です。
Joe、

あなたとhwiechersは私に2番目の推測をさせていただきました。月曜日に作業に取り掛かり、個々のマシンをより綿密に検査し、SQL Serverのバージョンが以前に報告されたとおりであることを確認します。
Yoopergeek 2009年

19
あなたとhwiechersは正しいです。顔全体に卵があります。手がかりで私を叩いてくれてありがとう。:)あなたが最初だったので、あなたは答えを得ます。ただし、1つの説明を付け加えたいのですが、SQL2008では複数の接続を開くことができますが、同時にはできません。同時に開いている接続は1つだけです。そうしないと、TransactionScopeがDTCにエスカレートします。
Yoopergeek 2009年

@Yoopergeekあなたの「同時にではない」が重要であることを確認し、それに応じて@Joeの回答を編集しました。テスト中にTCP接続を監視すると、接続が同時に使用されていない場合に古いTCP接続が再利用されるため、サーバー側のTransactionScope1つCOMMITで対処できるため、エスカレーションが不要になります。
Eugene Beresovsky 2013年

58

1
あなたの研究を共有してくれてありがとう。それは本当に役に立ちました。もう1つのクイッククエリ。TransactionScope()とsqlConnection.BeginTransaction()の違いは何ですか?
Baig、2011

この機能リクエストに応じて、ODAC 12CはSQL 2008として動作するようになり、同じデータソースへの連続した接続を使用するときに分散にプロモートされません。
フレデリック

31

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

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

SQL Server 2008の昇格可能なトランザクション

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

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

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


10

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

追加された4 8月2010〜で17:42 Eduardo

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

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


msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspxが見つかりません、Visual Studio 2005 Retired documentation
Kiquenet

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();
        }
    }

使用しているSQL Serverのエディション @Peter Meinlの回答は、2008R2やDenaliで行われた変更を反映するように更新する必要があるのでしょうか。
Yoopergeek、2012年

SQL Server 2008 R2を使用しています。
Iftikhar Ali

2008 R2の方が動作が良いのでしょうか。@hwiechersの回答では、コンパイル対象のフレームワークのバージョンがエスカレーションを妨げているのかどうかも疑問に思います。最後に、それがローカルのR2インスタンスであることは何か違いがあるのでしょうか。私は、これは2008 R2とSQL Server 2012のリリースでどのように変化したかを調査するために、時間/リソースを持っていたい
Yoopergeek

ネストされた接続が問題かどうかわからない?LOL ...よく咲いてそれを削除してください!、なぜ地球上で絶対に必要ではないのにステートメントを使用してネストするのですか?
Paul Zahra 2013

1

内部で複数の接続にアクセスする場合、TransactionScopeは常にDTCトランザクションにエスカレートします。上記のコードがDTCを無効にして動作する唯一の方法は、大きな可能性として、接続プールから同じ接続を両方取得した場合です。

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


0

connectionStringがプーリングをfalseに設定していないことを確認してください。これにより、TransactionScopeの新しいSqlConnectionごとに新しい接続が作成され、DTCにエスカレートされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.