web-dev-qa-db-ja.com

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

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

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

私は本当に頭を悩ませるようになり、ADO.NETトランザクションオブジェクトに基づくホームスパンのTransactionScopeのようなソリューションへのロールバックを真剣に考えています。それは一見狂っているようです-開発者の半分doesで機能する(そしてエスカレートしない)同じコードが他の開発者でエスカレートします。

トランザクションが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
  • Dev 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を簡単に渡すことができない状況では問題があるようです。ピュー!

アップデート3

ここで質問で明確にするために:

SQL2008:

  • 1つのTransactionScope内で複数の接続を許可します(上記のサンプルコードで説明されています)。
  • 警告1:これらの複数のSqlConnectionがネストされている場合、つまり、2つ以上のSqlConnectionが同時に開かれている場合、TransactionScopeはすぐにDTCにエスカレートします。
  • 警告2:追加のSqlConnectionが別の'durable resource'(つまり、別のSQL Server)に対して開かれている場合、すぐにDTCにエスカレートします

SQL2005:

  • 単一のTransactionScope期間内に複数の接続を許可しません。 2番目のSqlConnectionが開かれた場合、またはその場合にエスカレートします。

更新4

この質問をさらにするために 混乱の わかりやすくするために、SQL-2005をsingleSqlConnectionでDTCにエスカレートする方法を以下に示します。

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

これは壊れているように見えますが、SqlConnection.Open()へのすべての呼び出しが接続プールから取得されているかどうかは理解できると思います。

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

そのため、基本的に、TransactionScopeをSQL2005で正常に使用するには、最初のTransactionScopeが不要になるまでインスタンス化された時点から開いたままの何らかのグローバル接続オブジェクトが必要です。グローバル接続オブジェクトのコード臭に加えて、最初に接続を開き、最後に接続を閉じることは、できるだけ遅く接続を開き、できるだけ早く閉じるという論理に反します。

279
Yoopergeek

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

開発者の中にはSQL Server 2005を使用している人もいれば、SQL Server 2008を使用している人もいます。エスカレートしている開発者とそうでない開発者を正しく特定していますか?

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

71
Joe

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

enter image description here

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

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

58
Peter Meinl

そのコードは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およびバージョン3.5の.NET Frameworkから、以前のトランザクションが閉じられた後にトランザクションで別の接続が開かれた場合、ローカルトランザクションは分散トランザクションに昇格しなくなりました。既に接続プールとトランザクションへの参加を使用している場合、これはコードの変更を必要としません。

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

31
hwiechers

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

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

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

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

10
Chris Marisic

ネストされた接続が問題であるかどうかはあまりわかりません。 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();
        }
    }
2
Iftikhar Ali

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

「トラブルは、開発者のマシンの半分で、MSDTCを無効にして実行できることです。」無効になっていますか?;)

1
amateur