Wie verwende ich Transaktionen mit dapper.net?


106

Ich möchte mehrere Einfügeanweisungen für mehrere Tabellen ausführen. Ich benutze dapper.net. Ich sehe keine Möglichkeit, Transaktionen mit dapper.net abzuwickeln.

Bitte teilen Sie Ihre Ideen zur Verwendung von Transaktionen mit dapper.net mit.

Antworten:


107

Hier das Code-Snippet:

using System.Transactions;    
....    
using (var transactionScope = new TransactionScope())
{
    DoYourDapperWork();
    transactionScope.Complete();
}

Beachten Sie, dass Sie einen Verweis auf die System.TransactionsAssembly hinzufügen müssen, da standardmäßig nicht darauf verwiesen wird.


7
Ist es notwendig, bei Fehlern explizit ein Rollback durchzuführen, oder behandelt System.Transactions dies automatisch?
Norbert Norbertson

6
@NorbertNorbertson es macht es automatisch, in Dispose()Methode. Wenn Complete()nicht aufgerufen wurde, wird die Transaktion zurückgesetzt.
the_joric

4
Erwähnenswert wegen einer anderen Antwort ( stackoverflow.com/a/20047975/47672 ): Die Verbindung muss innerhalb des TransctionScopeVerwendungsblocks geöffnet werden, falls Sie diese Antwort wählen.
0x49D1

2
Siehe auch ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Ausführen, Abfragen usw.) benötigt die Transaktion in den Parametern.
Matthieu

Wird der Rollback automatisch aufgerufen, wenn ein Problem vorliegt?
Gandalf

91

Ich habe es vorgezogen, einen intuitiveren Ansatz zu verwenden, indem ich die Transaktion direkt von der Verbindung erhalten habe:

// This called method will get a connection, and open it if it's not yet open.
using (var connection = GetOpenConnection())
using (var transaction = connection.BeginTransaction())
{
    connection.Execute(
        "INSERT INTO data(Foo, Bar) values (@Foo, @Bar);", listOf5000Items, transaction);
    transaction.Commit();
}

@ ANeves: Nun, wir verwenden wahrscheinlich verschiedene Dapper-Frameworks, weil dieses hat: github.com/StackExchange/dapper-dot-net
andrecarlucci

25
muss connection.open () vor .begintransaction aufrufen
Timeless

Eine Verbindung wird nicht automatisch im Transaktionsbereich registriert, es sei denn, Sie öffnen die Verbindung im Transaktionsbereich. Ich weiß nicht, wie Ihr Code funktioniert, wenn sich GetOpenConnection auf magische Weise innerhalb des Transaktionsbereichs öffnet, aber ich würde wetten, dass dies nicht der Fall ist
Erik Bergstedt

@ErikBergstedt, sagen Sie, dass die Verbindung erst geöffnet werden darf , nachdem wir sie aufgerufen .BeginTransaction()haben? Wenn dies der Fall wäre, würde diese Erweiterungsmethode eine falsche Verwendung der Transaktion fördern. (IMO, es sollte sogar werfen "kann Transaktion nicht öffnen, nachdem die Verbindung bereits geöffnet ist".)
ANeves

2
Ein guter Punkt, um die Transaktion als Parameter aufzunehmen Execute, da dies erforderlich ist.
Arve Systad

19

Sie sollten in der Lage sein zu verwenden, TransactionScopeda Dapper nur ADO.NET-Befehle ausführt.

using (var scope = new TransactionScope())
{
   // insert
   // insert
   scope.Complete();
}

8

Da sich alle Ihre Tabellen in einer einzigen Datenbank befinden, bin ich mit der TransactionScopein einigen Antworten hier vorgeschlagenen Lösung nicht einverstanden . Verweisen Sie auf diese Antwort.

  1. TransactionScopewird im Allgemeinen für verteilte Transaktionen verwendet; Transaktionen, die sich über verschiedene Datenbanken erstrecken, können sich auf verschiedenen Systemen befinden. Dies erfordert einige Konfigurationen unter Betriebssystem und SQL Server, ohne die dies nicht funktioniert. Dies wird nicht empfohlen, wenn sich alle Ihre Abfragen auf eine einzelne Datenbankinstanz beziehen.
    Bei einer einzelnen Datenbank kann dies jedoch hilfreich sein, wenn Sie den Code in eine Transaktion aufnehmen müssen, die nicht unter Ihrer Kontrolle steht. Bei einer einzelnen Datenbank sind auch keine speziellen Konfigurationen erforderlich.

  2. connection.BeginTransactionist die ADO.NET-Syntax zum Implementieren von Transaktionen (in C #, VB.NET usw.) für eine einzelne Datenbank. Dies funktioniert nicht über mehrere Datenbanken hinweg.

Also, connection.BeginTransaction()ist ein besserer Weg.

Noch besser ist es, UnitOfWork zu implementieren, wie in dieser Antwort erläutert .


4
Man braucht nicht mehrere Datenbanken, um von TransactionScope zu profitieren. Von besonderem Nutzen ist die Umgebungstemperatur. Es eignet sich hervorragend zum Umschließen von Code, den Sie nicht besitzen oder den Sie nicht ändern können, in eine Transaktion. Zum Beispiel kann es sehr effektiv verwendet werden, wenn Code für Unit- / Integrationstests Datenbankaufrufe ausführt, bei denen Sie ein Rollback durchführen möchten. Schweben Sie einfach ein TransactionScope, testen Sie den Code und entsorgen Sie ihn während der Testbereinigung.
Larry Smith

3
@ LarrySmith: Einverstanden; aber die Frage ist über nichts davon. OP sagt nur, dass er in einer Transaktion mehrere Tabellen einfügen möchte. Einige Antworten, einschließlich der akzeptierten, schlagen vor, eine zu verwenden, TransactionScopedie für das, was OP will, ineffizient ist. Ich bin damit einverstanden, dass dies TransactionScopein vielen Fällen ein gutes Werkzeug ist. aber nicht das.
Amit Joshi

5

Daniels Antwort funktionierte wie erwartet für mich. Der Vollständigkeit halber hier ein Ausschnitt, der Commit und Rollback anhand eines Transaktionsbereichs und eines Dappers demonstriert:

using System.Transactions;
    // _sqlConnection has been opened elsewhere in preceeding code 
    using (var transactionScope = new TransactionScope())
    {
        try
        {
            long result = _sqlConnection.ExecuteScalar<long>(sqlString, new {Param1 = 1, Param2 = "string"});

            transactionScope.Complete();
        }
        catch (Exception exception)
        {
            // Logger initialized elsewhere in code
            _logger.Error(exception, $"Error encountered whilst executing  SQL: {sqlString}, Message: {exception.Message}")

            // re-throw to let the caller know
            throw;
        }
    } // This is where Dispose is called 

2
@usr das kommt auf persönliche Vorlieben an. Ich möchte lieber wissen, wann etwas zum ersten Mal schief gelaufen ist, und sehe die Protokollanweisungen nicht als Abfall. Außerdem zeigt meine Antwort immer noch Wert, indem sie eine Möglichkeit demonstriert, Transaktionen mit
dapper

@CodeNaked, zuerst hast du dort die falsche Reihenfolge. Der catch-Block wird zuerst getroffen, wenn es eine Ausnahme gibt, und dann das Ende des Anwendungsbereichs. Schauen Sie sich diese Antwort und das MSDN-Dokument an, auf das verwiesen wird: stackoverflow.com/a/5306896/190476 Aufruf ein zweites Mal entsorgen ist nicht schädlich, ein gut gestaltetes Objekt ignoriert den zweiten Aufruf. Die Ablehnung ist nicht gerechtfertigt!
Sudhanshu Mishra

@dotnetguy - Ich habe nicht versucht zu kommunizieren, welche DisposeMethode zuerst oder zweitens aufgerufen wird, nur dass sie zweimal aufgerufen wird. Was den Punkt betrifft, dass "ein zweites Mal Dispose nicht schädlich ist", ist dies eine große Annahme. Ich habe gelernt, dass die Dokumente und die tatsächlichen Implementierungen oft nicht übereinstimmen. Aber wenn Sie das Wort von Microsoft dafür wollen: msdn.microsoft.com/en-us/library/…
CodeNaked

3
Eine Warnung zur Codeanalyse ist also Ihr Grund für eine Ablehnung? Das macht die Antwort nicht falsch oder irreführend - dann ist eine Ablehnung angemessen. Warum bearbeiten Sie die Antwort nicht und schlagen eine bessere Lösung vor, während Sie die Funktionalität beibehalten? Beim Stapelüberlauf geht es um Hilfe und konstruktive Kritik.
Sudhanshu Mishra
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.