Comment utiliser les transactions avec dapper.net?

106

Je voudrais exécuter plusieurs instructions d'insertion sur plusieurs tables. J'utilise dapper.net. Je ne vois aucun moyen de gérer les transactions avec dapper.net.

Veuillez partager vos idées sur la façon d'utiliser les transactions avec dapper.net.

Amit
la source

Réponses:

107

Voici l'extrait de code:

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

Notez que vous devez ajouter une référence à l' System.Transactionsassembly car il n'est pas référencé par défaut.

the_joric
la source
7
Est-il nécessaire de revenir explicitement en arrière en cas d'erreur ou System.Transactions gère-t-il cela automatiquement?
Norbert Norbertson
6
@NorbertNorbertson il le fait automatiquement, en Dispose()méthode. S'il Complete()n'a pas été appelé, la transaction est annulée.
the_joric
4
À mentionner à cause d'une autre réponse ( stackoverflow.com/a/20047975/47672 ): la connexion doit être ouverte à l'intérieur du TransctionScopebloc using au cas où vous choisiriez cette réponse.
0x49D1
2
Voir aussi ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Execute, Query, etc ...) a besoin de la transaction dans les paramètres.
Matthieu
La restauration est-elle appelée automatiquement en cas de problème?
gandalf
91

J'ai préféré utiliser une approche plus intuitive en récupérant la transaction directement depuis la connexion:

// 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
la source
@ANeves: Eh bien, nous utilisons probablement différents frameworks Dapper, car celui-ci a: github.com/StackExchange/dapper-dot-net
andrecarlucci
25
devez appeler connection.open () avant .begintransaction
Timeless
Une connexion n'est pas automatiquement inscrite dans transactionscope, sauf si vous ouvrez la connexion dans transactionscope. Je ne sais pas comment votre code fonctionne, si GetOpenConnection s'ouvre d'une manière ou d'une autre par magie dans le périmètre de transaction, mais je parie que ce n'est pas le cas
Erik Bergstedt
@ErikBergstedt, êtes-vous en train de dire que la connexion ne doit être ouverte qu'après l' appel .BeginTransaction()? Si tel était le cas, cette méthode d'extension favoriserait une mauvaise utilisation de la transaction. (OMI, il devrait même lancer "
Impossible d'
2
Bon point d'inclure la transaction en tant que paramètre dans Execute, car cela est nécessaire.
Arve Systad
19

Vous devriez pouvoir l'utiliser TransactionScopecar Dapper n'exécute que des commandes ADO.NET.

using (var scope = new TransactionScope())
{
   // insert
   // insert
   scope.Complete();
}
Daniel A. White
la source
8

Étant donné que toutes vos tables sont dans une seule base de données, je ne suis pas d'accord avec la TransactionScopesolution suggérée dans certaines réponses ici. Reportez - vous cette réponse.

  1. TransactionScopeest généralement utilisé pour les transactions distribuées; la transaction couvrant différentes bases de données peut être sur un système différent. Cela nécessite certaines configurations sur le système d'exploitation et SQL Server sans lesquelles cela ne fonctionnera pas. Cela n'est pas recommandé si toutes vos requêtes concernent une seule instance de base de données.
    Mais, avec une base de données unique, cela peut être utile lorsque vous devez inclure le code dans une transaction qui n'est pas sous votre contrôle. Avec une base de données unique, il n'a pas non plus besoin de configurations spéciales.

  2. connection.BeginTransactionest la syntaxe ADO.NET pour implémenter une transaction (en C #, VB.NET, etc.) sur une seule base de données. Cela ne fonctionne pas sur plusieurs bases de données.

Alors, connection.BeginTransaction()c'est la meilleure façon de procéder.

Même la meilleure façon de gérer la transaction consiste à implémenter UnitOfWork comme expliqué dans cette réponse.

Amit Joshi
la source
4
On n'a pas besoin de plusieurs bases de données pour bénéficier de TransactionScope. Il est particulièrement utile que ce soit ambiant. C'est idéal pour encapsuler du code que vous ne possédez pas ou ne pouvez pas modifier, dans une transaction. Par exemple, il peut être utilisé à bon escient lorsque le code de test d'unité / intégration qui effectue des appels de base de données à l'endroit où vous souhaitez revenir en arrière. Faites simplement flotter un TransactionScope, testez le code et supprimez-le pendant le nettoyage du test.
Larry Smith
3
@LarrySmith: D'accord; mais la question ne concerne rien de tout cela. OP dit simplement qu'il veut insérer dans plusieurs tables en une seule transaction. Certaines réponses, y compris celle acceptée, suggèrent d'utiliser TransactionScopece qui est inefficace pour ce que veut OP. Je reconnais que TransactionScopec'est un bon outil dans de nombreux cas; mais pas ça.
Amit Joshi
5

La réponse de Daniel a fonctionné comme prévu pour moi. Pour être complet, voici un extrait de code qui montre la validation et la restauration à l'aide d'une portée de transaction et d'un 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
la source
2
@usr qui se résume à vos préférences personnelles. Je préfère savoir la première fois que quelque chose a mal tourné et ne pas voir les déclarations de journal comme de la litière. En outre, ma réponse annonce toujours de la valeur en démontrant une façon d'utiliser les transactions avec dapper
Sudhanshu Mishra
@CodeNaked, d'abord, vous vous trompez dans l'ordre. Le bloc catch serait touché en premier s'il y a une exception, puis la fin de la portée d'utilisation. Deuxièmement, regardez cette réponse et le document MSDN référencé: stackoverflow.com/a/5306896/190476 appeler dispose une deuxième fois n'est pas dangereux, un objet bien conçu ignore le deuxième appel. Le vote défavorable n'est pas justifié!
Sudhanshu Mishra
@dotnetguy - Je n'ai pas essayé de communiquer quelle Disposeméthode est appelée en premier ou en deuxième, juste qu'elle est appelée deux fois. Quant au fait que "appeler à disposer une deuxième fois n'est pas nuisible", c'est une grande hypothèse. J'ai appris que les documents et les implémentations réelles ne sont souvent pas d'accord. Mais si vous voulez le mot de Microsoft pour cela: msdn.microsoft.com/en-us/library
...
3
Alors, un avertissement d'analyse de code est votre raison de voter contre? Cela ne rend pas la réponse fausse ou trompeuse - c'est à ce moment qu'un vote défavorable est approprié. Pourquoi ne pas éditer la réponse et proposer une meilleure solution tout en conservant la fonctionnalité? Le débordement de pile est une question d'aide et de critique constructive.
Sudhanshu Mishra