Jak korzystać z transakcji w dapper.net?

106

Chciałbym uruchomić wiele instrukcji wstawiania na wielu tabelach. Używam dapper.net. Nie widzę sposobu obsługi transakcji z dapper.net.

Podziel się swoimi pomysłami na wykorzystanie transakcji w dapper.net.

Amit
źródło

Odpowiedzi:

107

Tutaj fragment kodu:

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

Zauważ, że musisz dodać odwołanie do System.Transactionszestawu, ponieważ domyślnie nie ma do niego odwołania.

the_joric
źródło
7
Czy konieczne jest jawne wycofywanie zmian w przypadku błędu, czy też System.Transactions obsługuje to automatycznie?
Norbert Norbertson
6
@NorbertNorbertson robi to automatycznie, Dispose()metodą. Jeśli Complete()nie został wywołany, transakcja zostanie wycofana.
the_joric
4
Warto o tym wspomnieć z powodu innej odpowiedzi ( stackoverflow.com/a/20047975/47672 ): połączenie musi być otwarte wewnątrz TransctionScopebloku using w przypadku wybrania tej odpowiedzi.
0x49D1
2
Zobacz także ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Execute, Query, etc ...) wymaga transakcji w parametrach.
Matthieu
Czy wycofanie jest wywoływane automatycznie, jeśli wystąpi problem?
gandalf
91

Wolałem zastosować bardziej intuicyjne podejście, pobierając transakcję bezpośrednio z połączenia:

// 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
źródło
@ANeves: Cóż, prawdopodobnie używamy różnych frameworków Dapper, ponieważ ten ma: github.com/StackExchange/dapper-dot-net
andrecarlucci
25
trzeba zadzwonić na connection.open () przed .początkiem transakcji
Timeless
Połączenie nie jest automatycznie rejestrowane w transactioncope, chyba że otworzysz połączenie w transactioncope. Nie wiem, jak działa twój kod, jeśli GetOpenConnection w jakiś magiczny sposób otwiera się w zakresie transakcji, ale założę się, że tak się nie dzieje
Erik Bergstedt
@ErikBergstedt, czy mówisz, że połączenie musi być otwarte dopiero po wywołaniu .BeginTransaction()go? Gdyby tak było, ta metoda rozszerzenia promowałaby niewłaściwe wykorzystanie transakcji. (IMO, powinno nawet wyrzucić „nie można otworzyć transakcji po tym, jak połączenie jest już otwarte”.)
ANeves
2
Warto uwzględnić transakcję jako parametr Execute, ponieważ jest to wymagane.
Arve Systad
19

Powinieneś móc używać, TransactionScopeponieważ Dapper uruchamia tylko polecenia ADO.NET.

using (var scope = new TransactionScope())
{
   // insert
   // insert
   scope.Complete();
}
Daniel A. White
źródło
8

Biorąc pod uwagę, że wszystkie twoje tabele znajdują się w jednej bazie danych, nie zgadzam się z TransactionScoperozwiązaniem sugerowanym w niektórych odpowiedziach tutaj. Skorzystaj z tej odpowiedzi.

  1. TransactionScopejest zwykle używany do transakcji rozproszonych; transakcja obejmująca różne bazy danych może znajdować się w innym systemie. Wymaga to pewnych konfiguracji w systemie operacyjnym i SQL Server, bez których to nie zadziała. Nie jest to zalecane, jeśli wszystkie zapytania dotyczą jednej instancji bazy danych.
    Ale w przypadku jednej bazy danych może to być przydatne, gdy musisz uwzględnić kod w transakcji, nad którym nie masz kontroli. W przypadku pojedynczej bazy danych nie wymaga również specjalnych konfiguracji.

  2. connection.BeginTransactionto składnia ADO.NET do implementacji transakcji (w C #, VB.NET itp.) w pojedynczej bazie danych. Nie działa to w przypadku wielu baz danych.

Więc connection.BeginTransaction()jest lepszym sposobem.

Jeszcze lepszym sposobem obsługi transakcji jest wdrożenie UnitOfWork, jak wyjaśniono w tej odpowiedzi.

Amit Joshi
źródło
4
Nie potrzeba wielu baz danych, aby korzystać z TransactionScope. Szczególnie użyteczne jest to, że jest to ambient. Świetnie nadaje się do pakowania w transakcję kodu, którego nie jesteś właścicielem lub którego nie możesz zmodyfikować. Na przykład może być używany z doskonałym efektem, gdy kod testów jednostkowych / integracyjnych, który wykonuje wywołania bazy danych, gdy chcesz wycofać zmiany. Po prostu przenieś TransactionScope, przetestuj kod i usuń podczas czyszczenia testu.
Larry Smith
3
@LarrySmith: Zgoda; ale pytanie nie dotyczy tego wszystkiego. OP mówi tylko, że chce wstawić wiele tabel w jednej transakcji. Niektóre odpowiedzi, w tym zaakceptowana, sugerują użycie tego, TransactionScopeco jest nieefektywne dla tego, czego chce PO. Zgadzam się, że TransactionScopeto dobre narzędzie w wielu przypadkach; ale nie to.
Amit Joshi
5

Odpowiedź Daniela zadziałała dla mnie zgodnie z oczekiwaniami. Dla kompletności, oto fragment, który demonstruje zatwierdzanie i wycofywanie przy użyciu zakresu transakcji i dapper:

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 
Sudhanshu Mishra
źródło
2
@usr, co sprowadza się do osobistych preferencji. Wolę wiedzieć, kiedy pierwszy raz coś poszło nie tak i nie postrzegam zapisów dziennika jako śmieci. Ponadto moja odpowiedź nadal jest wartościowa, pokazując jeden sposób korzystania z transakcji z eleganckim
Sudhanshu Mishra
@CodeNaked, po pierwsze, masz niewłaściwą kolejność. Blok catch zostałby trafiony jako pierwszy, jeśli istnieje wyjątek, a następnie koniec zakresu do użycia. Po drugie, spójrz na tę odpowiedź i przywoływany dokument MSDN: stackoverflow.com/a/5306896/190476 wywołanie dispose po raz drugi nie jest szkodliwe, dobrze zaprojektowany obiekt ignoruje drugie wywołanie. Głos przeciw nie jest uzasadniony!
Sudhanshu Mishra
@dotnetguy - Nie próbowałem komunikować, która Disposemetoda jest wywoływana jako pierwsza czy druga, tylko że jest wywoływana dwukrotnie. Co do tego, że „wezwanie do pozbycia się po raz drugi nie jest szkodliwe”, to duże założenie. Dowiedziałem się, że dokumenty i rzeczywiste implementacje często nie są zgodne. Ale jeśli chcesz na to słowo Microsoftu: msdn.microsoft.com/en-us/library/ ...
CodeNaked
3
Więc ostrzeżenie dotyczące analizy kodu jest powodem do odrzucenia? Nie oznacza to, że odpowiedź jest błędna ani myląca - wtedy należy głosować przeciw. Dlaczego nie zmienisz odpowiedzi i nie zaproponujesz lepszego rozwiązania przy zachowaniu funkcjonalności? W przepełnieniu stosu chodzi o pomoc i konstruktywną krytykę.
Sudhanshu Mishra