none
Frage zu: Innerhalb eines TransactionScope eine neue zusätzliche SqlConnection() öffnen RRS feed

  • 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

    Samstag, 14. August 2010 15:49

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 2

    Der 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.aspx

    Was 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

    Montag, 16. August 2010 08:41
    Beantworter

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 2

    Der 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.aspx

    Was 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

    Montag, 16. August 2010 08:41
    Beantworter
  • 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

     

    Montag, 16. August 2010 12:37
  • Hallo Michael,

    verkomplizieren muß man nicht.
    Aber die MS DTC Konfiguration solltest Du Dir auf jeden Fall anschauen.
    Denn über kurz oder lang wirst Du damit zu tun kriegen.

    Gruß Elmar

    Montag, 16. August 2010 12:50
    Beantworter
  • Hallo

    ja - das ist mir schon klar - MS DTC ist konfiguriert, sonst würden ja die Transacations beim Entity-Framework nicht funktioniert.

    Schönen Gruß - Michael

    Montag, 16. August 2010 13:13
  • 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:

        internal 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();
            }
          }
        }
    
    
    Gruß Elmar

    Montag, 16. August 2010 14:00
    Beantworter