Загрузив компьютер сегодня утром, я столкнулся с точной проблемой для проекта, над которым я работаю. У меня были некоторые идеи, которые приводят к следующему дизайну - и комментарии были бы более чем удивительными. К сожалению, дизайн, предложенный Джошем, невозможен, поскольку я должен работать с удаленным SQL-сервером и не могу включить службу Distribute Transaction Coordinator, на которую он опирается.
Мое решение основано на нескольких, но простых изменениях в моем существующем коде.
Во-первых, у меня есть все мои хранилищами реализовать простой интерфейс маркера:
/// <summary>
/// A base interface for all repositories to implement.
/// </summary>
public interface IRepository
{ }
Во-вторых, я позволяю все мои сделки позволили хранилищами реализовать следующий интерфейс:
/// <summary>
/// Provides methods to enable transaction support.
/// </summary>
public interface IHasTransactions : IRepository
{
/// <summary>
/// Initiates a transaction scope.
/// </summary>
void BeginTransaction();
/// <summary>
/// Executes the transaction.
/// </summary>
void CommitTransaction();
}
Идея заключается в том, что в все мои репозитории я реализую этот интерфейс и добавляю код, который вводит транзакцию напрямую в зависимости от фактического провайдера (для поддельных репозиториев я составил список делегатов, которые выполняются при фиксации). Для LINQ к SQL было бы легко сделать реализации, такие как:
#region IHasTransactions Members
public void BeginTransaction()
{
_db.Transaction = _db.Connection.BeginTransaction();
}
public void CommitTransaction()
{
_db.Transaction.Commit();
}
#endregion
Это, конечно, требует, чтобы новый репозиторий класса создается для каждого потока, но это разумно для моего проекта.
Каждый метод, использующий репозиторий, должен вызывать BeginTransaction()
и EndTransaction()
, если репозиторий реализует IHasTransactions
. Чтобы сделать этот вызов еще проще, я придумал следующие расширения:
/// <summary>
/// Extensions for spawning and subsequently executing a transaction.
/// </summary>
public static class TransactionExtensions
{
/// <summary>
/// Begins a transaction if the repository implements <see cref="IHasTransactions"/>.
/// </summary>
/// <param name="repository"></param>
public static void BeginTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.BeginTransaction();
}
}
public static void CommitTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.CommitTransaction();
}
}
}
Комментарии оценены!
Я не думаю, что это решение в духе DDD. В основном вы создали сценарий транзакции, который выполняет работу с моделью домена. Например, служба не должна изменять статус клиента. –
Что-то в коде должно обрабатывать это бизнес-правило, будь то на этом уровне или на более высоком уровне, когда делались изменения в пределах одного TransactionScope, позволяющего обрабатывать транзакции локальными транзакциями или распределенными транзакциями.Если в бизнес-правиле говорится о том, что клиент обновляется каждый раз, когда размещается заказ, это хорошее место для обработки, поскольку все заказы проходят здесь. – JoshBerke