このプロジェクトでは、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();
}
本当に掘り下げて、これを理解しようとしました。動作するマシンに関する情報を次に示します。
動作しない開発者:
問題を解決するために、すべてのマシンにMicrosoft Updateから入手可能なすべてのパッチが完全に適用されていることを追加する必要があります。
そのMSDNトランザクションエスカレーションページには、次の条件によりトランザクションがDTCにエスカレートされることが記載されています。
#3は発生していません。 #2は一度に1つの接続しかないため、発生しません。また、単一の「耐久性のあるリソース」への接続でもあります。 #1が発生する可能性のある方法はありますか?単一フェーズの通知をサポートしないSQL2005/8構成がありますか?
個人的には、すべてのSQL Serverバージョンを再調査しました。「Dev 3」には実際にSQL2008があり、「Dev 4」には実際にはSQL2005があります。それは私の同僚を二度と信用しないことを教えてくれるでしょう。 ;)このデータの変更により、問題が見つかったと確信しています。 SQL2008にはSQL2005にはないものがたくさん含まれているため、SQL2008開発者はこの問題を経験していませんでした。
また、SQL2005をサポートするため、これまでのようにTransactionScopeを使用することはできず、TransactionScopeを使用する場合は、単一のSqlConnectionオブジェクトを渡す必要があることもわかります。これは、SqlConnectionを簡単に渡すことができない状況では問題があるようです。ピュー!
ここで質問で明確にするために:
SQL2008:
SQL2005:
この質問をさらにするために 混乱の わかりやすくするために、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が不要になるまでインスタンス化された時点から開いたままの何らかのグローバル接続オブジェクトが必要です。グローバル接続オブジェクトのコード臭に加えて、最初に接続を開き、最後に接続を閉じることは、できるだけ遅く接続を開き、できるだけ早く閉じるという論理に反します。
SQL Server 2008は、複数のSQLConnection
sを1つのTransactionScope
でエスカレートせずに使用できます。ただし、接続が同時に開いていない場合、複数の「物理」_TCP接続が発生するため、エスカレーションが必要です。
開発者の中にはSQL Server 2005を使用している人もいれば、SQL Server 2008を使用している人もいます。エスカレートしている開発者とそうでない開発者を正しく特定していますか?
最も明白な説明は、SQL Server 2008を使用する開発者はエスカレートしていないということです。
トピックに関する私の研究の結果:
分散トランザクションへの不要なエスカレーションを回避する を参照してください
私はまだOracleのエスカレーション動作を調査しています: 同じDBへの複数の接続にまたがるトランザクションはDTCにエスカレートしますか?
そのコードは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が失敗する理由を説明できません。それは逆ではないのですか?
この回答が削除された理由はわかりませんが、これにはいくつかの関連情報があるようです。
10年8月4日17:42に回答 エドゥアルド
接続文字列にEnlist = falseを設定して、トランザクションでの自動登録を回避します。
接続を参加者として手動で登録する トランザクションスコープ。 [ 元の記事 期限切れ]またはこれを行う: MSDTCの自動昇格を防ぐ方法[archive.is]
ネストされた接続が問題であるかどうかはあまりわかりません。 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();
}
}
内部で複数の接続にアクセスする場合、TransactionScopeは常にDTCトランザクションにエスカレートします。上記のコードがDTCを無効にして動作する唯一の方法は、接続プールから同じ接続を両方の機会に取得する可能性が非常に高い場合です。
「トラブルは、開発者のマシンの半分で、MSDTCを無効にして実行できることです。」無効になっていますか?;)