In unserem Projekt verwenden wir TransactionScope, um sicherzustellen, dass unsere Datenzugriffsschicht ihre Aktionen in einer Transaktion ausführt. Wir möchten nicht, dass der MSDTC-Dienst auf den Computern unserer Endbenutzer aktiviert wird.
Das Problem ist, dass wir auf der Hälfte unserer Entwicklercomputer mit deaktiviertem MSDTC arbeiten können. Die andere Hälfte muss aktiviert sein, sonst wird die Fehlermeldung "MSDTC auf [SERVER] ist nicht verfügbar" angezeigt .
Ich habe mich wirklich am Kopf gekratzt und ernsthaft darüber nachgedacht, auf eine hausgemachte TransactionScope-ähnliche Lösung zurückzugreifen, die auf ADO.NET-Transaktionsobjekten basiert. Es ist scheinbar verrückt - der gleiche Code , das funktioniert (und nicht eskaliert) auf die Hälfte unseres Entwicklers tut eskalieren auf der anderen Entwickler.
Ich hatte auf eine bessere Antwort auf Trace gehofft, warum eine Transaktion zu DTC eskaliert, aber leider nicht.
Hier ist ein Beispielcode, der die Probleme verursacht. Auf den Computern, die versuchen zu eskalieren, versucht er, bei der zweiten Verbindung zu eskalieren. Open () (und ja, es ist derzeit keine andere Verbindung offen.)
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();
}
Wir haben uns wirklich eingegraben und versucht, das herauszufinden. Hier sind einige Informationen zu den Maschinen, auf denen es funktioniert:
- Entwicklung 1: Windows 7 x64 SQL2008
- Dev 2: Windows 7 x86 SQL2008
- Dev 3: Windows 7 x64
SQL2005SQL2008
Entwickler, bei denen es nicht funktioniert:
- Dev 4: Windows 7 x64,
SQL2008SQL2005 - Dev 5: Windows Vista x86, SQL2005
- Dev 6: Windows XP X86, SQL2005
- Mein Heim-PC: Windows Vista Home Premium, x86, SQL2005
Ich sollte hinzufügen, dass alle Computer, um das Problem zu beheben, vollständig mit allem gepatcht wurden, was in Microsoft Update verfügbar ist.
Update 1:
- http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/ beschreibt ein ähnliches Problem ... bereits 2006!
- http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - Lesen Sie dieses Codebeispiel, es zeigt deutlich eine verschachtelte zweite Verbindung (zu einem zweiten SQL Server, tatsächlich), die zu DTC eskalieren wird. Wir tun dies nicht in unserem Code - wir verwenden weder unterschiedliche SQL Server noch unterschiedliche Verbindungszeichenfolgen, noch haben wir verschachtelte sekundäre Verbindungen geöffnet - es sollte keine Eskalation zum DTC geben .
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (ab 2005) erläutert, wie bei einer Verbindung mit SQL2000 immer eine Eskalation zu DTC auftritt. Wir verwenden SQL2005 / 2008
- http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN bei Transaktionseskalation.
Diese MSDN-Transaktionseskalationsseite gibt an, dass die folgenden Bedingungen dazu führen, dass eine Transaktion zu DTC eskaliert:
- Mindestens eine dauerhafte Ressource, die keine einphasigen Benachrichtigungen unterstützt, wird in die Transaktion aufgenommen.
- In die Transaktion werden mindestens zwei dauerhafte Ressourcen aufgenommen, die einphasige Benachrichtigungen unterstützen. Wenn Sie beispielsweise eine einzelne Verbindung mit registrieren, wird keine Transaktion hochgestuft. Wenn Sie jedoch eine zweite Verbindung zu einer Datenbank öffnen, wodurch die Datenbank registriert wird, erkennt die System.Transactions-Infrastruktur, dass es sich um die zweite dauerhafte Ressource in der Transaktion handelt, und eskaliert sie zu einer MSDTC-Transaktion.
- Eine Anforderung zum "Marshalling" der Transaktion in eine andere Anwendungsdomäne oder einen anderen Prozess wird aufgerufen. Zum Beispiel die Serialisierung des Transaktionsobjekts über eine Anwendungsdomänengrenze hinweg. Das Transaktionsobjekt wird nach Wert gemarshallt. Dies bedeutet, dass jeder Versuch, es über eine Anwendungsdomänengrenze (auch im selben Prozess) zu übergeben, zur Serialisierung des Transaktionsobjekts führt. Sie können die Transaktionsobjekte übergeben, indem Sie eine Remote-Methode aufrufen, die eine Transaktion als Parameter verwendet, oder Sie können versuchen, auf eine Remote-Transaktionskomponente zuzugreifen. Dies serialisiert das Transaktionsobjekt und führt zu einer Eskalation, wie wenn eine Transaktion über eine Anwendungsdomäne serialisiert wird. Es wird verteilt und der lokale Transaktionsmanager ist nicht mehr ausreichend.
Wir erleben nicht # 3. # 2 findet nicht statt, da immer nur eine Verbindung vorhanden ist und es sich auch um eine einzelne "dauerhafte Ressource" handelt. Gibt es eine Möglichkeit, dass # 1 passieren könnte? Eine SQL2005 / 8-Konfiguration, die dazu führt, dass einphasige Benachrichtigungen nicht unterstützt werden?
Update 2:
Persönlich wurden alle SQL Server-Versionen erneut untersucht - "Dev 3" hat tatsächlich SQL2008, und "Dev 4" ist tatsächlich SQL2005. Das wird mich lehren, meinen Mitarbeitern nie wieder zu vertrauen. ;) Aufgrund dieser Datenänderung bin ich mir ziemlich sicher, dass wir unser Problem gefunden haben. Unsere SQL2008-Entwickler hatten das Problem nicht, da SQL2008 eine Menge großartiger Funktionen enthält, die SQL2005 nicht hat.
Da wir SQL2005 unterstützen, können wir TransactionScope nicht mehr so verwenden wie bisher. Wenn wir TransactionScope verwenden möchten, müssen wir ein einzelnes SqlConnection-Objekt übergeben ... Dies scheint in Situationen problematisch zu sein, in denen die SqlConnection nicht einfach weitergegeben werden kann. Es riecht nur nach einer globalen SqlConnection-Instanz. Bank!
Update 3
Nur um hier in der Frage zu klären:
SQL2008:
- Ermöglicht mehrere Verbindungen innerhalb eines einzelnen TransactionScope (wie im obigen Beispielcode gezeigt).
- Vorsichtsmaßnahme Nr. 1: Wenn diese mehreren SqlConnections verschachtelt sind, dh zwei oder mehr SqlConnections gleichzeitig geöffnet werden, wird TransactionScope sofort zu DTC eskaliert.
- Vorsichtsmaßnahme Nr. 2: Wenn eine zusätzliche SqlConnection für eine andere 'dauerhafte Ressource' (dh einen anderen SQL Server) geöffnet wird, wird sie sofort zu DTC eskaliert
SQL2005:
- Ermöglicht nicht mehrere Verbindungen innerhalb eines einzelnen TransactionScope-Zeitraums. Es eskaliert, wenn eine zweite SqlConnection geöffnet wird.
Update 4
Im Interesse dieser Frage machen noch ein Durcheinander nützlich, und nur aus Gründen mehr Klarheit, hier, wie Sie SQL2005 mit einem zu DTC eskalieren bekommen können einzelne SqlConnection
:
using (TransactionScope transactionScope = new TransactionScope()) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
connection.Close();
connection.Open(); // escalates to DTC
}
}
Dies scheint mir nur kaputt zu sein, aber ich denke, ich kann verstehen, ob jeder Anruf SqlConnection.Open()
aus dem Verbindungspool stammt.
"Warum könnte das aber passieren?" Wenn Sie einen SqlTableAdapter für diese Verbindung verwenden, bevor sie geöffnet wird, öffnet und schließt der SqlTableAdapter die Verbindung und beendet die Transaktion effektiv für Sie, da Sie sie jetzt nicht erneut öffnen können.
Um TransactionScope mit SQL2005 erfolgreich verwenden zu können, muss im Grunde ein globales Verbindungsobjekt vorhanden sein, das ab dem Zeitpunkt der ersten Instanziierung von TransactionScope geöffnet bleibt, bis es nicht mehr benötigt wird. Neben dem Code-Geruch eines globalen Verbindungsobjekts widerspricht das Öffnen und Schließen der Verbindung zuerst der Logik, eine Verbindung so spät wie möglich zu öffnen und so schnell wie möglich zu schließen.
quelle
Antworten:
SQL Server 2008 kann mehrere
SQLConnection
s in einem verwenden,TransactionScope
ohne eskalieren zu müssen, vorausgesetzt, die Verbindungen sind nicht gleichzeitig geöffnet, was zu mehreren "physischen" TCP-Verbindungen führen und daher eine Eskalation erfordern würde.Ich sehe, dass einige Ihrer Entwickler SQL Server 2005 und andere SQL Server 2008 haben. Sind Sie sicher, dass Sie richtig identifiziert haben, welche eskalieren und welche nicht?
Die naheliegendste Erklärung wäre, dass Entwickler mit SQL Server 2008 nicht eskalieren.
quelle
TransactionScope
kann dies mit einer einzelnen VerbindungCOMMIT
auf der Serverseite auskommen , was eine Eskalation überflüssig machen würde.Das Ergebnis meiner Forschung zum Thema:
Siehe Vermeiden Sie unerwünschte Eskalationen zu verteilten Transaktionen
Ich untersuche immer noch das Eskalationsverhalten von Oracle: Werden Transaktionen, die mehrere Verbindungen zu derselben Datenbank umfassen, zu DTC eskaliert?
quelle
Dieser Code führt bei der Verbindung zu 2005 zu einer Eskalation.
Überprüfen Sie die Dokumentation zu MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx
Ich kann nicht erklären, warum Dev 3: Windows 7 x64, SQL2005 erfolgreich ist und Dev 4: Windows 7 x64 fehlschlägt. Sind Sie sicher, dass das nicht umgekehrt ist?
quelle
Ich weiß nicht, warum diese Antwort gelöscht wurde, aber dies scheint einige relevante Informationen zu haben.
antwortete am 4. August 10 um 17:42 Eduardo
Setzen Sie Enlist = false in der Verbindungszeichenfolge, um eine automatische Registrierung bei der Transaktion zu vermeiden.
Tragen Sie die Verbindung manuell als Teilnehmer im Transaktionsbereich ein. [ Originalartikel veraltet] oder tun Sie dies: So verhindern Sie die automatische MSDTC-Promotion [archive.is]
quelle
Ich bin mir nicht sicher, ob eine verschachtelte Verbindung das Problem ist. Ich rufe eine lokale Instanz von SQL Server auf und es wird kein DTC generiert.
quelle
TransactionScope eskaliert immer zur DTC-Transaktion, wenn Sie Zugriff auf mehr als eine Verbindung innerhalb verwenden. Der obige Code kann nur dann mit deaktiviertem DTC funktionieren, wenn Sie mit großer Wahrscheinlichkeit beide Male dieselbe Verbindung aus dem Verbindungspool erhalten.
"Das Problem ist, dass wir auf der Hälfte unserer Entwicklercomputer mit deaktiviertem MSDTC arbeiten können." Bist du sicher, dass es deaktiviert ist?
quelle
Stellen Sie sicher, dass Ihr connectionString das Pooling nicht auf false setzt. Dies führt zu einer neuen Verbindung für jede neue SqlConnection im TransactionScope und eskaliert sie zu DTC.
quelle