Как использовать транзакции с dapper.net?


106

Я хотел бы запустить несколько операторов вставки в нескольких таблицах. Я использую dapper.net. Я не вижу способа обрабатывать транзакции с dapper.net.

Поделитесь своими идеями о том, как использовать транзакции с dapper.net.

Ответы:


107

Вот фрагмент кода:

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

Обратите внимание, что вам нужно добавить ссылку на System.Transactionsсборку, потому что по умолчанию на нее нет ссылки.


7
Требуется ли явный откат при ошибке или System.Transactions обрабатывает это автоматически?
Норберт Норбертсон

6
@NorbertNorbertson делает это автоматически, Dispose()методом. Если Complete()не был вызван, транзакция откатывается.
the_joric 04

4
Стоит упомянуть из-за другого ответа ( stackoverflow.com/a/20047975/47672 ): соединение должно быть открыто внутри TransctionScopeблока using, если вы выберете этот ответ.
0x49D1 08

2
См. Также ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Execute, Query и т. Д.) Требует транзакции в параметрах.
Matthieu

Откат вызывается автоматически при возникновении проблемы?
gandalf 08

91

Я предпочел использовать более интуитивный подход, получая транзакцию непосредственно из соединения:

// 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: Что ж, мы, вероятно, используем разные фреймворки Dapper, потому что у этого есть: github.com/StackExchange/dapper-dot-net
andrecarlucci

25
необходимо вызвать connection.open () перед .begintransaction
Timeless

Соединение не включается автоматически в область транзакции, если вы не открываете соединение в области транзакции. Я не знаю, как работает ваш код, если GetOpenConnection каким-то волшебным образом открывается в области транзакций, но держу пари, что это не так,
Эрик Бергстедт,

@ErikBergstedt, вы говорите, что соединение должно быть открыто только после того, как мы его вызовем .BeginTransaction()? Если бы это было так, этот метод расширения способствовал бы неправильному использованию транзакции. (IMO, он должен даже бросить «не удается открыть транзакцию после того, как соединение уже открыто».)
ANeves

2
Хороший момент для включения транзакции в качестве параметра Execute, если это необходимо.
Arve Systad

19

Вы должны иметь возможность использовать, TransactionScopeпоскольку Dapper запускает только команды ADO.NET.

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

8

Учитывая, что все ваши таблицы находятся в единой базе данных, я не согласен с TransactionScopeрешением, предложенным в некоторых ответах здесь. Обратитесь к этому ответу.

  1. TransactionScopeобычно используется для распределенных транзакций; транзакции, охватывающие разные базы данных, могут находиться в разных системах. Это требует некоторых настроек операционной системы и SQL Server, без которых это не будет работать. Это не рекомендуется, если все ваши запросы относятся к одному экземпляру базы данных.
    Но с единой базой данных это может быть полезно, когда вам нужно включить код в транзакцию, которая не находится под вашим контролем. С единой базой данных тоже не требуется особых настроек.

  2. connection.BeginTransactionсинтаксис ADO.NET для реализации транзакции (в C #, VB.NET и т. д.) с единой базой данных. Это не работает с несколькими базами данных.

Итак, connection.BeginTransaction()лучший способ пойти.

Даже лучший способ обработать транзакцию - реализовать UnitOfWork, как описано в этом ответе.


4
Чтобы воспользоваться TransactionScope, не требуется несколько баз данных. Особая полезность заключается в том, что он окружающий. Он отлично подходит для упаковки кода, который вам не принадлежит или который вы не можете изменить, в транзакцию. Например, его можно использовать для большого эффекта, когда код модульного / интеграционного тестирования выполняет вызовы базы данных, после чего вы хотите выполнить откат. Просто разместите TransactionScope, протестируйте код и удалите во время тестовой очистки.
Ларри Смит

3
@LarrySmith: Согласен; но вопрос не ни в чём. OP просто говорит, что хочет вставить в несколько таблиц за одну транзакцию. Некоторые ответы, включая принятый, предлагают использовать, TransactionScopeкоторый неэффективен для того, что хочет OP. Я согласен, что TransactionScopeво многих случаях это хороший инструмент; но не это.
Амит Джоши

5

Ответ Дэниела сработал для меня, как и ожидалось. Для полноты, вот фрагмент, который демонстрирует фиксацию и откат с использованием области транзакции и 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 зависит от личных предпочтений. Я предпочитаю знать, когда что-то пошло не так в первый раз, и не рассматриваю записи журнала как мусор. Кроме того, мой ответ по-прежнему имеет ценность, демонстрируя один из способов использования транзакций с
dapper

@CodeNaked, во-первых, у вас неправильный порядок. Если есть исключение, сначала будет обработан блок catch, а затем - конец области действия. Во-вторых, взгляните на этот ответ и указанный документ MSDN: stackoverflow.com/a/5306896/190476, вызывающий удаление во второй раз, не вреден, хорошо спроектированный объект игнорирует второй вызов. Голос против не оправдан!
Судханшу Мишра

@dotnetguy - я не пытался сообщить, какой Disposeметод вызывается первым или вторым, просто он вызывается дважды. Что касается того, что «повторный вызов утилиты не вреден», это большое предположение. Я узнал, что документы и фактические реализации часто не совпадают. Но если вам нужны слова Microsoft: msdn.microsoft.com/en-us/library/…
CodeNaked

3
Итак, предупреждение анализа кода - ваша причина голосовать против? Это не делает ответ неправильным или вводящим в заблуждение - вот тогда уместно отрицательное голосование. Почему бы вам не отредактировать ответ и не предложить лучшее решение, сохранив при этом функциональность? Stack overflow - это помощь и конструктивная критика.
Судханшу Мишра,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.