Risposte:
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.
Dispose()metodo. Se Complete()non è stato chiamato, la transazione viene annullata.
TransctionScopeblocco utilizzando nel caso in cui si scelga questa risposta.
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();
}
.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".)
Execute, poiché è necessario.
Dovresti essere in grado di usare TransactionScopepoiché Dapper esegue solo comandi ADO.NET.
using (var scope = new TransactionScope())
{
// insert
// insert
scope.Complete();
}
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.
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.
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.
TransactionScopeinefficiente per ciò che vuole l'OP. Sono d'accordo che TransactionScopein molti casi è un buon strumento; ma non questo.
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
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/...