Benutzer mit den meisten Antworten
Frage zu: Innerhalb eines TransactionScope eine neue zusätzliche SqlConnection() öffnen

Frage
-
Hallo
ich komme bei mir auf ein Problem, wenn ich in meiner Applikation innerhalb eines
using (TransactionScope tran = new TransactionScope()) { ............. ............. .............. }
meine Tätigkeiten mit den Entity-Framework-Objekten abarbeite, und anschließend/innerhalb eine (älter) Methode einer Static-Hilfsklasse aufrufe, die Protokoll-Daten in die Datenbank schreibt - in dieser Methode wird noch via ADO.net mit einem SqlCommand ein INSERT-Statement erzeugt. Die Applikation stürzt bei
try { conn = new SqlConnection(ConnectionString); conn.Open(); } catch (Exception ex) {
conn.Open() ab mit Fehlermeldung:
- Aufgrund von Kommunikationsproblemen konnte der MSDTC-Transaktions-Manager die
Transaktion nicht vom Quelltransaktions-Manager übernehmen.
Mögliche Ursachen: Es ist eine Firewall vorhanden, für die für den MSDTC-Prozess keine Ausnahme
festgelegt wurde, die Computer können sich nicht anhand ihrer
NetBIOS-Namen finden, oder die Unterstützung von Netzwerktransaktionen ist
für einen der beiden Transaktions-Manager nicht aktiviert. (Exception from HRESULT: 0x8004D02B)
Das Programm läuft ohne Probleme mit der Transaction durch, wenn ich diesen Aufruf deaktiviere (MSDTC ist aktiviert) - Transaction mit den Entity-Framework-Objekten funktioniert.
Ich denke es liegt an dem, dass ich hier noch eine weitere SqlConnection öffnen möchte - meine Frage, ob dies überhaupt möglich ist, oder sollte ich meine Protokoll-Schreib-Tätikeiten auch auf EF. umschreiben (oder das Protokoll erst nach dem TransactionScope schreiben) ??
Vielen Dank für eine Info zu meiner Frage!!
Schönen Gruß
Michael
- Aufgrund von Kommunikationsproblemen konnte der MSDTC-Transaktions-Manager die
Antworten
-
Hallo Michael,
sicher darfst Du mehr als eine Verbindung verwenden.
Ein Mischen von unterschiedlichen Schnittstellen - hier Entity Framework und SqlCommand - ist dabei ohne
weiteres möglich. Auch das Entity Framework basiert im Kern auf dem Sql Client und nimmt transparent
an Transaktionen teil.Nur sobald Du eine zweite Verbindung verwendest, wird die Transaction zu einer verteilten heraufgestuft,
und damit kommt MS DTC ins Spiel, siehe Integration von 'System.Transactions' in SQL Server (ADO.NET)
Verteilte Transaktionen sind grundsätzlich um einiges langsamer, so dass man sie vermeiden sollte,
so sie nicht erforderlich sind.Ein Funktionieren von MS DTC sollte man bei Einsatz von System.Transactions jedoch immer sicherstellen.
Was derzeit der Meldung nach nicht gegegeben ist. Wirf mal die Komponentendienste (dcomcnfg.exe) an.
Unter Komponentendienste -> Arbeitsplatz -> Eigenschaften (Kontextmenü) findest Du die aktuellen
Einstellungen. Nach dem Fehlercode dürfte die Netzwerkkonfiguration nicht korrekt sein, z. B.:
http://www.bunkerhollow.com/blogs/matt/archive/2009/10/26/network-access-for-distributed-transaction-manager-msdtc-has-been-disabled.aspx
Wobei mit Windows XP SP2 und allem später dies im Standard blockiert ist:
New functionality in the Distributed Transaction Coordinator service in Windows Server 2003 Service Pack 1
and in Windows XP Service Pack 2Der Rest hängt von Deiner Rechnerkonfiguration ab. Beschwert sich der MS DTC weiterhin,
hilft dtcping bei der Diagnose, siehe
http://blogs.msdn.com/b/distributedservices/archive/2008/11/12/troubleshooting-msdtc-issues-with-the-dtcping-tool.aspxWas die Protokoll Anweisung angeht, hängt es davon ab, inwieweit sie an der Transaktion teilnehmen soll.
Wenn sie ausserhalb der Transaktion immer geschrieben werden soll, so wäre TransactionScopeOption.Suppress
eine Alternative:internal static void DoLog(string meldung) { using (var scope = new TransactionScope(TransactionScopeOption.Suppress)) { using (var connection = new SqlConnection(connectionString)) { connection.Open(); using (var command = new SqlCommand("INSERT INTO dbo.Protokoll(Meldung) VALUES (@Meldung);", connection)) { command.Parameters.Add("@Meldung", System.Data.SqlDbType.NVarChar, 255).Value = meldung; command.ExecuteNonQuery(); } } scope.Complete(); } }
ohne Suppress würde mit dem Connection.Open eine Heraufstufen als verteilte Transaktion geschehen.
Was man u. a. in den Komponentendiensten in der Transaktionliste sieht.Gruß Elmar
- Bearbeitet Elmar BoyeEditor Montag, 16. August 2010 08:44 DtcPing
- Als Antwort markiert M.Erlinger Montag, 16. August 2010 12:37
Alle Antworten
-
Hallo Michael,
sicher darfst Du mehr als eine Verbindung verwenden.
Ein Mischen von unterschiedlichen Schnittstellen - hier Entity Framework und SqlCommand - ist dabei ohne
weiteres möglich. Auch das Entity Framework basiert im Kern auf dem Sql Client und nimmt transparent
an Transaktionen teil.Nur sobald Du eine zweite Verbindung verwendest, wird die Transaction zu einer verteilten heraufgestuft,
und damit kommt MS DTC ins Spiel, siehe Integration von 'System.Transactions' in SQL Server (ADO.NET)
Verteilte Transaktionen sind grundsätzlich um einiges langsamer, so dass man sie vermeiden sollte,
so sie nicht erforderlich sind.Ein Funktionieren von MS DTC sollte man bei Einsatz von System.Transactions jedoch immer sicherstellen.
Was derzeit der Meldung nach nicht gegegeben ist. Wirf mal die Komponentendienste (dcomcnfg.exe) an.
Unter Komponentendienste -> Arbeitsplatz -> Eigenschaften (Kontextmenü) findest Du die aktuellen
Einstellungen. Nach dem Fehlercode dürfte die Netzwerkkonfiguration nicht korrekt sein, z. B.:
http://www.bunkerhollow.com/blogs/matt/archive/2009/10/26/network-access-for-distributed-transaction-manager-msdtc-has-been-disabled.aspx
Wobei mit Windows XP SP2 und allem später dies im Standard blockiert ist:
New functionality in the Distributed Transaction Coordinator service in Windows Server 2003 Service Pack 1
and in Windows XP Service Pack 2Der Rest hängt von Deiner Rechnerkonfiguration ab. Beschwert sich der MS DTC weiterhin,
hilft dtcping bei der Diagnose, siehe
http://blogs.msdn.com/b/distributedservices/archive/2008/11/12/troubleshooting-msdtc-issues-with-the-dtcping-tool.aspxWas die Protokoll Anweisung angeht, hängt es davon ab, inwieweit sie an der Transaktion teilnehmen soll.
Wenn sie ausserhalb der Transaktion immer geschrieben werden soll, so wäre TransactionScopeOption.Suppress
eine Alternative:internal static void DoLog(string meldung) { using (var scope = new TransactionScope(TransactionScopeOption.Suppress)) { using (var connection = new SqlConnection(connectionString)) { connection.Open(); using (var command = new SqlCommand("INSERT INTO dbo.Protokoll(Meldung) VALUES (@Meldung);", connection)) { command.Parameters.Add("@Meldung", System.Data.SqlDbType.NVarChar, 255).Value = meldung; command.ExecuteNonQuery(); } } scope.Complete(); } }
ohne Suppress würde mit dem Connection.Open eine Heraufstufen als verteilte Transaktion geschehen.
Was man u. a. in den Komponentendiensten in der Transaktionliste sieht.Gruß Elmar
- Bearbeitet Elmar BoyeEditor Montag, 16. August 2010 08:44 DtcPing
- Als Antwort markiert M.Erlinger Montag, 16. August 2010 12:37
-
Hallo Elmar
vielen Dank für die auführliche Rückmeldung. Ich werde die ganze Sache nicht zu sehr verkomplizieren - d.h. ich schreibe mir die Protokoll-Einträge während meines TransactionScope für Entity-Framework-Objekte im Speicher mit, und nach dem TransactioScope übertrage ich die List<x> via der alten Methode in die Datenbank.
Gruß - Michael
-
Hallo Michael,
da wäre ich mir nicht so sicher, wenn ich Deine Fehlermeldung so anschaue.
Solange Du nur eine Verbindung hat, werden "leichtgewichtige" Transaktionen verwendet,
vergleichbar mit SqlTransaction die lokal vom SQL Server verwaltet werden.
Erst wenn mehrere Verbindungen zusammengefaßt werden müssen, kommt MS DTC zum Tragen.Und ob es sich um das EF handelt ist dafür irrelevant.
Ein Mini-Test in Anlehnung an heute morgen:Gruß Elmarinternal static void UseTransactionScope() { string connectionString = Properties.Settings.Default.NorthwindConnectionString; using (var scope = new TransactionScope()) { using (var connection = new SqlConnection(connectionString)) { connection.Open(); using (var command = new SqlCommand("SELECT @@TRANCOUNT;", connection)) { int trancount = (int)command.ExecuteScalar(); } DoLog("Eine Meldung"); } scope.Complete(); } } internal static void DoLog(string meldung) { string connectionString = Properties.Settings.Default.NorthwindConnectionString; using (var connection = new SqlConnection(connectionString)) { // Meckert er hier oder nicht? connection.Open(); using (var command = new SqlCommand("SELECT @@TRANCOUNT;", connection)) { int trancount = (int)command.ExecuteScalar(); } } }