Come utilizzare le transazioni con dapper.net?


106

Vorrei eseguire più istruzioni di inserimento su più tabelle. Sto usando dapper.net. Non vedo alcun modo per gestire le transazioni con dapper.net.

Per favore condividi le tue idee su come usare le transazioni con dapper.net.

Risposte:


107

Qui lo snippet di codice:

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

Si noti che è necessario aggiungere un riferimento System.Transactionsall'assembly perché non è referenziato per impostazione predefinita.


7
È necessario eseguire il rollback esplicito in caso di errore o System.Transactions lo gestisce automaticamente?
Norbert Norbertson

6
@NorbertNorbertson lo fa automaticamente, nel Dispose()metodo. Se Complete()non è stato chiamato, la transazione viene annullata.
the_joric

4
Degno di nota a causa di un'altra risposta ( stackoverflow.com/a/20047975/47672 ): la connessione deve essere aperta all'interno del TransctionScopeblocco utilizzando nel caso in cui si scelga questa risposta.
0x49D1

2
Vedi anche ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Esegui, Query, ecc ...) ha bisogno l'operazione nei parametri.
Matthieu

Il rollback viene chiamato automaticamente in caso di problemi?
gandalf

91

Ho preferito utilizzare un approccio più intuitivo ottenendo la transazione direttamente dalla connessione:

// 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: Beh, probabilmente stiamo usando diversi framework Dapper, perché questo ha: github.com/StackExchange/dapper-dot-net
andrecarlucci

25
chiamare connection.open () prima di .begintransaction
Timeless

Una connessione non viene automaticamente inserita in Transactioncope a meno che non si apra la connessione all'interno di Transactioncope. Non so come funziona il tuo codice, se GetOpenConnection in qualche modo si apre magicamente all'interno dell'ambito delle transazioni, ma scommetto che non è così
Erik Bergstedt

@ErikBergstedt, stai dicendo che la connessione deve essere aperta solo dopo averla richiamata .BeginTransaction()? In tal caso, questo metodo di estensione promuoverebbe un utilizzo errato della transazione. (IMO, dovrebbe persino lanciare "Impossibile aprire la transazione dopo che la connessione è già aperta".)
ANeves

2
Un buon punto per includere la transazione come parametro in Execute, poiché è necessario.
Arve Systad

19

Dovresti essere in grado di usare TransactionScopepoiché Dapper esegue solo comandi ADO.NET.

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

8

Considerando che tutte le tue tabelle sono in un unico database, non sono d'accordo con la TransactionScopesoluzione suggerita in alcune risposte qui. Riferisci questa risposta.

  1. TransactionScopeè generalmente utilizzato per transazioni distribuite; le transazioni che si estendono su database diversi possono trovarsi su sistemi diversi. Ciò richiede alcune configurazioni sul sistema operativo e SQL Server senza le quali non funzionerà. Questa operazione non è consigliata se tutte le query riguardano una singola istanza del database.
    Ma, con un database singolo, questo può essere utile quando è necessario includere il codice nella transazione che non è sotto il tuo controllo. Con un unico database, non necessita nemmeno di configurazioni speciali.

  2. connection.BeginTransactionè la sintassi di ADO.NET per implementare la transazione (in C #, VB.NET ecc.) su un singolo database. Non funziona su più database.

Così, connection.BeginTransaction() è il modo migliore per andare.

Anche il modo migliore per gestire la transazione è implementare UnitOfWork come spiegato in questa risposta.


4
Non sono necessari più database per beneficiare di TransactionScope. Di particolare utilità è che è ambient. È ottimo per il wrapping di codice che non possiedi o non puoi modificare, in una transazione. Ad esempio, può essere utilizzato con grande efficacia quando il codice di test di unità / integrazione che esegue chiamate al database in cui si desidera eseguire il rollback dopo. Basta spostare un TransactionScope, testare il codice e smaltirlo durante la pulizia di prova.
Larry Smith

3
@ LarrySmith: d'accordo; ma la domanda non riguarda niente di tutto questo. OP dice solo che vuole inserire in più tabelle in una transazione. Alcune risposte, inclusa quella accettata, suggeriscono di utilizzare quella TransactionScopeinefficiente per ciò che vuole l'OP. Sono d'accordo che TransactionScopein molti casi è un buon strumento; ma non questo.
Amit Joshi

5

La risposta di Daniel ha funzionato come previsto per me. Per completezza, ecco uno snippet che dimostra il commit e il rollback utilizzando un ambito di transazione e 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 

2
@usr che si riduce alle preferenze personali. Preferisco sapere la prima volta che qualcosa è andato storto e non vedere le dichiarazioni di registro come rifiuti. Inoltre, la mia risposta ha ancora valore pubblicitario dimostrando un modo per utilizzare le transazioni con dapper
Sudhanshu Mishra

@ CodeNaked, in primo luogo, hai sbagliato l'ordine. Il blocco catch verrebbe colpito per primo se c'è un'eccezione, quindi la fine dell'ambito per l'utilizzo. In secondo luogo, guarda questa risposta e il documento MSDN a cui si fa riferimento: stackoverflow.com/a/5306896/190476 chiamare dispose una seconda volta non è dannoso, un oggetto ben progettato ignora la seconda chiamata. Il voto negativo non è giustificato!
Sudhanshu Mishra

@dotnetguy - Non stavo cercando di comunicare quale Disposemetodo viene chiamato primo o secondo, solo che viene chiamato due volte. Quanto al punto che "chiamare smaltire una seconda volta non è dannoso", questo è un grande presupposto. Ho imparato che i documenti e le implementazioni effettive spesso non sono d'accordo. Ma se si vuole la parola di Microsoft per esso: msdn.microsoft.com/en-us/library/...
CodeNaked

3
Quindi, un avviso di analisi del codice è il motivo per cui decidi di votare? Ciò non rende la risposta sbagliata o fuorviante: è allora che un voto negativo è appropriato. Perché non modifichi la risposta e proponi una soluzione migliore mantenendo la funzionalità? Stack overflow si basa sull'aiuto e sulla critica costruttiva.
Sudhanshu Mishra
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.