私たちのプロジェクトでは、TransactionScope を使用して、データ アクセス層がトランザクション内でアクションを実行するようにしています。エンド ユーザーのマシンで MSDTC サービスを有効にする必要がないようにすることを目指しています。
問題は、開発者のマシンの半分では、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
SQL2005SQL2008
動作しない開発者:
- 開発 4: Windows 7 x64、
SQL2008SQL2005 - 開発 5: Windows Vista x86、SQL2005
- 開発 6: Windows XP X86、SQL2005
- 自宅の PC: Windows Vista Home Premium、x86、SQL2005
問題を突き止めるために、すべてのマシンに Microsoft Update から入手できるすべてのパッチが完全に適用されていることを付け加えておきます。
アップデート1:
- http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/2006 年にも同様の問題が発生していました。
- http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx- このコード サンプルを読むと、ネストされた 2 番目の接続 (実際には 2 番目の SQL サーバーへの接続) が明確に示され、これが DTC にエスカレートされます。私たちのコードではこれを行っていません。異なる SQL サーバーも、異なる接続文字列も使用しておらず、ネストされた 2 番目の接続も開いていません。DTCへのエスカレーションは発生しないはずです。
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx(2005年から)SQL2000に接続するとDTCへのエスカレーションが常に発生する方法について説明しています。私たちはSQL2005/2008を使用しています。
- http://msdn.microsoft.com/en-us/library/ms229978.aspxトランザクション エスカレーションに関する MSDN。
MSDN のトランザクション エスカレーション ページには、次の条件によりトランザクションが DTC にエスカレートされると記載されています。
- 単一フェーズ通知をサポートしていない永続リソースが少なくとも 1 つトランザクションに登録されています。
- 単一フェーズ通知をサポートする少なくとも 2 つの永続リソースがトランザクションに登録されます。たとえば、単一の接続を登録しても、トランザクションは昇格されません。ただし、データベースへの 2 番目の接続を開いてデータベースを登録すると、System.Transactions インフラストラクチャは、それがトランザクション内の 2 番目の永続リソースであることを検出し、MSDTC トランザクションにエスカレートします。
- トランザクションを別のアプリケーション ドメインまたは別のプロセスに「マーシャリング」する要求が呼び出されます。たとえば、アプリケーション ドメイン境界を越えたトランザクション オブジェクトのシリアル化などです。トランザクション オブジェクトは値によってマーシャリングされるため、アプリケーション ドメイン境界を越えて渡そうとすると (同じプロセス内であっても)、トランザクション オブジェクトがシリアル化されます。トランザクション オブジェクトは、トランザクションをパラメータとして受け取るリモート メソッドを呼び出すことで渡すことができます。また、リモートのトランザクション サービス コンポーネントにアクセスすることもできます。これにより、トランザクション オブジェクトがシリアル化され、トランザクションがアプリケーション ドメインを越えてシリアル化されるときのようにエスカレーションが発生します。分散されているため、ローカル トランザクション マネージャはもはや適切ではありません。
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:
- 単一の TransactionScope 内で複数の接続を許可します (上記のサンプル コードに示されているように)。
- 注意 #1: 複数の SqlConnection がネストされている場合、つまり 2 つ以上の SqlConnection が同時に開かれている場合、TransactionScope はすぐに DTC にエスカレートされます。
- 注意 #2: 追加の SqlConnection が別の「永続的なリソース」 (つまり、別の SQL Server) に対して開かれると、すぐに DTC にエスカレートされます。
SQL2005:
- 単一の TransactionScope 内で複数の接続は許可されません。2 番目の SqlConnection が開かれると、エスカレートされます。
アップデート4
この質問をさらにわかりやすく
するために 、またより明確にするために、SQL2005 を1 つの SqlConnection
コマンドで 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 によって接続が開かれて閉じられ、再度開くことができなくなるため、トランザクションが事実上終了します。
したがって、基本的に、SQL2005 で TransactionScope を正常に使用するには、最初の TransactionScope がインスタンス化された時点から不要になるまで開いたままになる、何らかのグローバル接続オブジェクトが必要です。グローバル接続オブジェクトのコード臭に加えて、最初に接続を開いて最後に閉じることは、接続をできるだけ遅く開いてできるだけ早く閉じるというロジックに反します。
ベストアンサー1
SQL Server 2008 では、接続が同時に開かれていない限り、エスカレーションせずに複数のSQLConnection
を 1 つにまとめることができますTransactionScope
。接続が同時に開かれると、複数の「物理的な」 TCP 接続が発生し、エスカレーションが必要になります。
開発者の中には SQL Server 2005 を使用している人もいれば、SQL Server 2008 を使用している人もいます。エスカレーションする開発者とエスカレーションしない開発者を正しく識別していますか?
最も明白な説明は、SQL Server 2008 を使用している開発者がエスカレーションを行っていないということでしょう。