Вы можете добавить параметр транзакции в конец методов, которые вы хотите запустить в транзакции, и присвоить ему значение по умолчанию, равное нулю. Таким образом, если вы не хотите запускать метод в существующей транзакции, не указывайте параметр end или явно передайте значение null.
Внутри этих методов вы можете проверить параметр на null, чтобы определить, создавать ли новую транзакцию или использовать другую переданную. Эта логика может быть передана, например, в базовый класс.
Это сохраняет ваши методы более чистыми, чем при использовании решения на основе контекста, хотя последнее, вероятно, лучше работает для универсальной библиотеки. Однако в автономном приложении вы знаете, какие методы необходимо связать в цепочку внутри транзакции, и это не все из них.
void Update(int itemId, string text, IDbTransaction trans = null) =>
RunInTransaction(ref trans, () =>
{
trans.Connection.Update("...");
});
void RunInTransaction(ref IDbTransaction transaction, Action f)
{
if (transaction == null)
{
using (var conn = DatabaseConnectionFactory.Create())
{
conn.Open();
using (transaction = conn.BeginTransaction())
{
f();
transaction.Commit();
}
}
}
else
{
f();
}
}
Update(1, "Hello World!");
Update(1, "Hello World!", transaction);
Тогда у вас может быть обработчик транзакций для вашего уровня обслуживания ...
public void RunInTransaction(Action<IDbTransaction> f)
{
using (var conn = DatabaseConnectionFactory.Create())
{
conn.Open();
using (var transaction = conn.BeginTransaction())
{
f(transaction);
transaction.Commit();
}
}
}
А сервисный метод может выглядеть так ...
void MyServiceMethod(int itemId, string text1, string text2) =>
transactionRunner.RunInTransaction(trans =>
{
repos.UpdateSomething(itemId, text1, trans);
repos.UpdateSomethingElse(itemId, text2, trans);
});
Что легко подделать для юнит-тестирования ...
public class MockTransactionRunner : ITransactionRunner
{
public void RunInTransaction(Action<IDbTransaction> f) => f(null);
}