Como usar transações com dapper.net?


106

Eu gostaria de executar várias instruções de inserção em várias tabelas. Estou usando o dapper.net. Não vejo nenhuma maneira de lidar com transações com dapper.net.

Compartilhe suas idéias sobre como usar transações com dapper.net.

Respostas:


107

Aqui está o snippet de código:

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

Observe que você precisa adicionar referência ao System.Transactionsassembly porque ele não é referenciado por padrão.


7
É necessário reverter explicitamente o erro ou System.Transactions lida com isso automaticamente?
— Norbert Norbertson,

6
@NorbertNorbertson faz isso automaticamente, no Dispose()método. Se Complete()não tiver sido chamado, a transação será revertida.
— the_joric

4
Vale a pena mencionar por causa de outra resposta ( stackoverflow.com/a/20047975/47672 ): a conexão deve ser aberta dentro do TransctionScopebloco de uso caso você escolha esta resposta.
— 0x49D1

2
Veja também ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Execute, Query, etc ...) precisa da transação nos parâmetros.
— Matthieu

O rollback é chamado automaticamente se houver um problema?
— gandalf

91

Eu preferi usar uma abordagem mais intuitiva, obtendo a transação diretamente da conexão:

// 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: Bem, provavelmente estamos usando frameworks Dapper diferentes, porque este tem: github.com/StackExchange/dapper-dot-net
— andrecarlucci

25
tem que chamar connection.open () antes de .begintransaction
— Timeless

Uma conexão não é automaticamente inscrita no escopo de transações a menos que você abra a conexão dentro do escopo de transações. Não sei como seu código funciona, se GetOpenConnection de alguma forma se abre magicamente dentro do escopo de transações, mas aposto que não
— Erik Bergstedt

@ErikBergstedt, você está dizendo que a conexão deve ser aberta somente após a chamada .BeginTransaction()? Se fosse esse o caso, esse método de extensão promoveria o uso incorreto da transação. (IMO, deve até lançar "não é possível abrir a transação depois que a conexão já estiver aberta".)
— ANeves

2
Um bom ponto para incluir a transação como um parâmetro no Execute, pois isso é obrigatório.
— Arve Systad

19

Você deve ser capaz de usar, TransactionScopejá que o Dapper executa apenas comandos ADO.NET.

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

8

Considerando que todas as suas tabelas estão em um único banco de dados, discordo da TransactionScopesolução sugerida em algumas respostas aqui. Consulte esta resposta.

  1. TransactionScopegeralmente é usado para transações distribuídas; transações abrangendo bancos de dados diferentes podem estar em sistemas diferentes. Isso precisa de algumas configurações no sistema operacional e no SQL Server, sem as quais não funcionará. Isso não é recomendado se todas as suas consultas forem em uma única instância do banco de dados.
    Mas, com um único banco de dados, isso pode ser útil quando você precisa incluir o código na transação que não está sob seu controle. Com um único banco de dados, também não precisa de configurações especiais.

  2. connection.BeginTransactioné a sintaxe ADO.NET para implementar a transação (em C #, VB.NET etc.) em um único banco de dados. Isso não funciona em vários bancos de dados.

Portanto, connection.BeginTransaction()é o melhor caminho a percorrer.

Mesmo a melhor maneira de lidar com a transação é implementar UnitOfWork conforme explicado nesta resposta.


4
Não são necessários vários bancos de dados para se beneficiar do TransactionScope. De particular utilidade é que é ambiente. É ótimo para agrupar código que você não possui ou não pode modificar em uma transação. Por exemplo, pode ser usado com grande efeito quando o código de teste de unidade / integração que faz chamadas de banco de dados onde você deseja reverter depois. Apenas coloque um TransactionScope, teste o código e descarte durante a limpeza de teste.
— Larry Smith

3
@LarrySmith: Concordo; mas a questão não é sobre nada disso. OP apenas diz que deseja inserir em várias tabelas em uma transação. Algumas respostas inclusive a aceita, sugerem o uso TransactionScopeque é ineficiente para o que o OP deseja. Concordo que TransactionScopeé uma boa ferramenta em muitos casos; mas não isso.
— Amit Joshi

5

A resposta de Daniel funcionou conforme o esperado para mim. Para completar, aqui está um snippet que demonstra o commit e rollback usando um escopo de transação 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 que se resume à preferência pessoal. Prefiro saber a primeira vez que algo deu errado e não ver as declarações do log como lixo. Além disso, minha resposta ainda
— agrega

@CodeNaked, primeiro, você entendeu a ordem errada. O bloco catch seria atingido primeiro se houver uma exceção e, em seguida, o fim do escopo de uso. Em segundo lugar, observe esta resposta e o documento do MSDN referenciado: stackoverflow.com/a/5306896/190476 chamar dispose uma segunda vez não é prejudicial, um objeto bem projetado ignora a segunda chamada. O downvote não se justifica!
— Sudhanshu Mishra

@dotnetguy - Não tentei comunicar qual Disposemétodo é chamado primeiro ou segundo, apenas que é chamado duas vezes. Quanto ao ponto de que "ligar para o descarte uma segunda vez não é prejudicial", é uma grande suposição. Aprendi que os documentos e as implementações reais muitas vezes não concordam. Mas se você quiser a palavra da Microsoft sobre isso: msdn.microsoft.com/en-us/library/…
— CodeNaked

3
Portanto, um aviso de análise de código é o seu motivo para votar negativamente? Isso não torna a resposta errada ou enganosa - é quando um voto negativo é apropriado. Por que você não edita a resposta e propõe uma solução melhor, mantendo a funcionalidade? Stack Overflow tem tudo a ver com ajuda e crítica construtiva.
— Sudhanshu Mishra
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.