Transaktionen in .net


144

Was sind die Best Practices für Transaktionen in C # .Net 2.0? Welche Klassen sollten verwendet werden? Was sind die Fallstricke, auf die man achten muss usw. All das Commit- und Rollback-Zeug. Ich starte gerade ein Projekt, in dem ich möglicherweise einige Transaktionen ausführen muss, während ich Daten in die Datenbank einfüge. Alle Antworten oder Links für grundlegende Informationen zu Transaktionen sind willkommen.


Hier ist ein gutes Beispiel für Transaktionen in .NET im Codeprojekt, die als Start verwendet werden sollen.
Mitchel Sellers

Antworten:


271

Es gibt zwei Hauptarten von Transaktionen; Verbindungstransaktionen und Umgebungstransaktionen. Eine Verbindungstransaktion (z. B. SqlTransaction) ist direkt mit der Datenbankverbindung (z. B. SqlConnection) verknüpft. Dies bedeutet, dass Sie die Verbindung weitergeben müssen - in einigen Fällen in Ordnung, aber "Erstellen / Verwenden / Freigeben" nicht zulassen. Verwendung und erlaubt keine db-übergreifende Arbeit. Ein Beispiel (für Leerzeichen formatiert):

using (IDbTransaction tran = conn.BeginTransaction()) {
    try {
        // your code
        tran.Commit();
    }  catch {
        tran.Rollback();
        throw;
    }
}

Nicht zu chaotisch, aber auf unsere Verbindung "conn" beschränkt. Wenn wir verschiedene Methoden aufrufen wollen, müssen wir jetzt "conn" weitergeben.

Die Alternative ist eine Umgebungstransaktion. Neu in .NET 2.0 ermöglicht das TransactionScope- Objekt (System.Transactions.dll) die Verwendung über eine Reihe von Vorgängen (geeignete Anbieter werden automatisch in die Umgebungstransaktion aufgenommen). Dies erleichtert das Nachrüsten in vorhandenen (nicht transaktionalen) Code und das Sprechen mit mehreren Anbietern (obwohl DTC beteiligt wird, wenn Sie mit mehr als einem sprechen).

Beispielsweise:

using(TransactionScope tran = new TransactionScope()) {
    CallAMethodThatDoesSomeWork();
    CallAMethodThatDoesSomeMoreWork();
    tran.Complete();
}

Beachten Sie hier, dass die beiden Methoden ihre eigenen Verbindungen verarbeiten können (Öffnen / Verwenden / Schließen / Entsorgen), jedoch stillschweigend Teil der Umgebungstransaktion werden, ohne dass wir etwas übergeben müssen.

Wenn Ihr Code fehlerhaft ist, wird Dispose () ohne Complete () aufgerufen, sodass ein Rollback durchgeführt wird. Die erwartete Verschachtelung usw. wird unterstützt, obwohl Sie eine innere Transaktion noch nicht zurücksetzen können, um die äußere Transaktion abzuschließen: Wenn jemand unglücklich ist, wird die Transaktion abgebrochen.

Der andere Vorteil von TransactionScope ist, dass es nicht nur an Datenbanken gebunden ist. Jeder transaktionsbewusste Anbieter kann es verwenden. WCF zum Beispiel. Oder es gibt sogar einige TransactionScope-kompatible Objektmodelle (z. B. .NET-Klassen mit Rollback-Funktion - möglicherweise einfacher als ein Andenken, obwohl ich diesen Ansatz selbst nie verwendet habe).

Alles in allem ein sehr, sehr nützliches Objekt.

Einige Einschränkungen:

  • Unter SQL Server 2000 wechselt ein TransactionScope sofort zum DTC. Dies ist in SQL Server 2005 und höher behoben. Es kann den LTM (viel weniger Overhead) verwenden, bis Sie mit 2 Quellen usw. sprechen, wenn er auf DTC erhöht wird.
  • Es gibt einen Fehler , der bedeutet, dass Sie möglicherweise Ihre Verbindungszeichenfolge anpassen müssen

CSLA .NET 2.0 unterstützt das TransactionScope-Objekt!
Binoj Antony

Das Problem hierbei ist, wenn Sie eine Transaktion in der ersten Methode haben und diese Methode (Kapselung) nicht weiß, ob sie von einer übergeordneten Transaktion aufgerufen wird oder nicht.
Eduardo Molteni

1
@Eduardo - das ist kein Problem bei der Verwendung von TransactionScope, was es sehr attraktiv macht. Solche Transaktionen werden verschachtelt und nur die äußersten Commits.
Marc Gravell

Ich hoffe du hörst immer noch zu. Sie sagten, dass es "einige TransactionScope-kompatible Objektmodelle gibt". Können Sie mich auf einige von ihnen hinweisen? Vielen Dank.
Majkinetor

1
Wieder Marc, eine weitere hervorragende Erklärung. Wenn Sie sagen, dass die erwartete Verschachtelung unterstützt wird, gilt dies für Transaktionsblöcke, die in den Methoden (z. B. CallAMethodThatDoesSomeWork ()) selbst definiert sind? Oder mit dem außerhalb definierten Transaktionsbereich ist es nicht erforderlich?
Phil Cooper

11
protected void Button1_Click(object sender, EventArgs e)
   {


       using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
       {
           connection1.Open();

           // Start a local transaction.
           SqlTransaction sqlTran = connection1.BeginTransaction();

           // Enlist a command in the current transaction.
           SqlCommand command = connection1.CreateCommand();
           command.Transaction = sqlTran;

           try
           {
               // Execute two separate commands.
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
               command.ExecuteNonQuery();
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
               command.ExecuteNonQuery();

               // Commit the transaction.
               sqlTran.Commit();
               Label3.Text = "Both records were written to database.";
           }
           catch (Exception ex)
           {
               // Handle the exception if the transaction fails to commit.
               Label4.Text = ex.Message;


               try
               {
                   // Attempt to roll back the transaction.
                   sqlTran.Rollback();
               }
               catch (Exception exRollback)
               {
                   // Throws an InvalidOperationException if the connection 
                   // is closed or the transaction has already been rolled 
                   // back on the server.
                   Label5.Text = exRollback.Message;

               }
           }
       }


   }

4

Sie können die Transaktion auch in eine eigene gespeicherte Prozedur packen und auf diese Weise behandeln, anstatt Transaktionen in C # selbst auszuführen.


1

Wenn Sie es nur für db-bezogene Inhalte benötigen, unterstützen einige OR-Mapper (z. B. NHibernate) standardmäßig Transactinos.


0

Es hängt auch davon ab, was Sie brauchen. Bei einfachen SQL-Transaktionen können Sie versuchen, TSQL-Transaktionen durchzuführen, indem Sie BEGIN TRANS und COMMIT TRANS in Ihrem Code verwenden. Dies ist der einfachste Weg, hat jedoch Komplexität und Sie müssen vorsichtig sein, um ein ordnungsgemäßes Commit (und einen Rollback) durchzuführen.

Ich würde so etwas benutzen

SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
    ...
    Do SQL stuff here passing my trans into my various SQL executers
    ...
    trans.Commit  // May not be quite right
}

Jeder Fehler führt dazu, dass Sie sofort aus usingder Transaktion aussteigen und die Transaktion immer festgeschrieben oder zurückgesetzt wird (je nachdem, was Sie ihm sagen). Das größte Problem, mit dem wir konfrontiert waren, war sicherzustellen, dass es immer begangen wurde. Die Verwendung stellt sicher, dass der Umfang der Transaktion begrenzt ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.