Відповіді:
Ось фрагмент коду:
using System.Transactions;
....
using (var transactionScope = new TransactionScope())
{
DoYourDapperWork();
transactionScope.Complete();
}
Зауважте, що вам потрібно додати посилання на System.Transactions
збірку, оскільки воно не посилається за замовчуванням.
Dispose()
методом. Якщо Complete()
не було викликано, транзакція повертається назад.
TransctionScope
блоку, якщо ви виберете цю відповідь.
Я вважав за краще використовувати більш інтуїтивний підхід, отримуючи транзакцію безпосередньо з підключення:
// 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
неефективні для того, що хочуть ОП. Я згоден, що TransactionScope
це гарний інструмент у багатьох випадках; але не це.
Відповідь Даніеля спрацювала так, як очікувалося для мене. Для повноти, ось фрагмент, який демонструє фіксацію та відкат, використовуючи область транзакції та даппер:
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/…