Ответы:
Вот фрагмент кода:
using System.Transactions;
....
using (var transactionScope = new TransactionScope())
{
DoYourDapperWork();
transactionScope.Complete();
}
Обратите внимание, что вам нужно добавить ссылку на System.Transactionsсборку, потому что по умолчанию на нее нет ссылки.
Dispose()методом. Если Complete()не был вызван, транзакция откатывается.
TransctionScopeблока using, если вы выберете этот ответ.
Я предпочел использовать более интуитивный подход, получая транзакцию непосредственно из соединения:
// 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()? Если бы это было так, этот метод расширения способствовал бы неправильному использованию транзакции. (IMO, он должен даже бросить «не удается открыть транзакцию после того, как соединение уже открыто».)
Execute, если это необходимо.
Вы должны иметь возможность использовать, TransactionScopeпоскольку Dapper запускает только команды ADO.NET.
using (var scope = new TransactionScope())
{
// insert
// insert
scope.Complete();
}
Учитывая, что все ваши таблицы находятся в единой базе данных, я не согласен с TransactionScopeрешением, предложенным в некоторых ответах здесь. Обратитесь к этому ответу.
TransactionScopeобычно используется для распределенных транзакций; транзакции, охватывающие разные базы данных, могут находиться в разных системах. Это требует некоторых настроек операционной системы и SQL Server, без которых это не будет работать. Это не рекомендуется, если все ваши запросы относятся к одному экземпляру базы данных.
Но с единой базой данных это может быть полезно, когда вам нужно включить код в транзакцию, которая не находится под вашим контролем. С единой базой данных тоже не требуется особых настроек.
connection.BeginTransactionсинтаксис ADO.NET для реализации транзакции (в C #, VB.NET и т. д.) с единой базой данных. Это не работает с несколькими базами данных.
Итак, connection.BeginTransaction()лучший способ пойти.
Даже лучший способ обработать транзакцию - реализовать UnitOfWork, как описано в этом ответе.
TransactionScopeкоторый неэффективен для того, что хочет OP. Я согласен, что TransactionScopeво многих случаях это хороший инструмент; но не это.
Ответ Дэниела сработал для меня, как и ожидалось. Для полноты, вот фрагмент, который демонстрирует фиксацию и откат с использованием области транзакции и 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
Disposeметод вызывается первым или вторым, просто он вызывается дважды. Что касается того, что «повторный вызов утилиты не вреден», это большое предположение. Я узнал, что документы и фактические реализации часто не совпадают. Но если вам нужны слова Microsoft: msdn.microsoft.com/en-us/library/…