Взаимодействие между шаблонами единицы работы и репозитория

Прочитав множество статей, я все еще не уверен в ответственности шаблона Unit of Work при взаимодействии с репозиториями.

Репозитории отвечают за загрузку и сохранение корневых объектов Aggregate, поэтому рассмотрим следующий пример кода:

using(IUnitOfWork uow = container.CreateUnitOfWork())
{
     Repository<ARoot> roots = container.GetRepository<ARoot>();
     ARoot root = root.FindByName("ARoot");
     root.Name = "ANewName";

     roots.Save(root);
     uow.Commit();
}

Интерфейс единицы работы будет определен следующими методами:

public interface IUnitOfWork
{
     void Insert(object);
     void Update(object);
     void Delete(object);
     void Commit();
     void Rollback();
}

Предположим, что репозиторий реализован с использованием очень простого SQL Mapper, поэтому FindByName содержит некоторый прямой SQL для возврата ARoot, будет ли реализация Save выглядеть примерно так:

public void Save(T entity)
{
      IUnitOfWork uow = GetUnitOfWork();
      // Tell the UOW we are updating this entity
      uow.Update(entity);
}

Затем код Unit Of Work Commit создаст весь необходимый SQL для сопоставления объекта обратно в БД?

Вопрос 2)

Если я добавлю совокупный корень в единицу работы, будет ли единица работы отвечать за сохранение корня и его дочерних объектов, или метод Save репозитория должен добавлять измененные объекты в единицу работы? например

public void Save(T entity)
{
      IUnitOfWork uow = GetUnitOfWork();
      // Tell the UOW we are updating this entity
      uow.Update(entity);
      uow.Update(entity.AChildObject);
}

ИЛИ... Альтернативно

Работает ли единица работы только с совокупными корнями и при фиксации вызывает методы сохранения репозитория для каждого объекта в его наборе изменений, сохраняя код сопоставления SQL для сохранения объекта в репозитории, изменяя первый пример кода на

using(IUnitOfWork uow = container.CreateUnitOfWork())
{
     Repository<ARoot> roots = container.GetRepository<ARoot>();
     ARoot root = root.FindByName("ARoot");
     root.Name = "ANewName";

     //roots.Save(root);
     uow.Update(root);
     // and commit
     uow.Commit();
}

Спасибо,

Джеймс


person James Simpson    schedule 21.01.2010    source источник


Ответы (3)


arrow_upward
4
arrow_downward

В нашем проекте мы используем репозиторий, чтобы вести себя точно так же, как набор сущностей, а UnitOfWork используется для отслеживания изменений в этих сущностях и для их записи обратно в хранилище данных.

Если вы используете LinqToSql или какой-либо другой OR Mapper, то он, скорее всего, реализует шаблон UnitOfWork сам по себе, поэтому часто мы просто используем экземпляр ORMapper в нашем собственном IUnitOfWork.

Интерфейс нашего репозитория обычно выглядит примерно так..

  IEnumerable<Order> FindByCustomerId(string customerId);
  void Add(Order order);
  void Remove(Order order);

У нас нет метода сохранения в репозитории. Если нам не нужен UnitOfWork, то методы Add/Remove воздействуют непосредственно на хранилище данных.

Если нам нужен UnitOfWork, то публичный интерфейс что-то вроде...

void Commit();
void Rollback();

Репозиторий имеет внутренний интерфейс с UnitOfWork, поэтому, когда мы запрашиваем репозиторий, возвращенные объекты отслеживаются UnitOfWork на наличие изменений. Метод фиксации записывает изменения обратно в хранилище данных, метод отката просто очищает их.

Когда мы используем LinqToSql, DataContext заботится об отслеживании изменений, при откате мы просто создаем экземпляр нового контекста. Постоянство обрабатывается как в корне, так и в его дочерних элементах. Один экземпляр UnitOfWork является общим для всех репозиториев.

Когда мы не используем LinqToSql, тогда мы реализуем свой собственный UnitOfWork, может быть, он вызывает веб-сервис или что-то еще, в этом случае мы отслеживаем изменения в самих классах сущностей, используя класс EntityBase.

У нас есть репозиторий для каждого корня, но иногда потомки одного корня сами используются в качестве корней, поэтому нам часто нужно что-то вроде OrderLineRepository, потому что в нашей системе есть вариант использования, когда пользователь хочет искать строки Order.

person Andronicus    schedule 01.02.2010

arrow_upward
3
arrow_downward

Обычно мне нравится видеть, как это делается, когда UoW отслеживает изменения, которые сохраняются, напрямую вызывая IRepository.Save().

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

Фиксация единицы работы - это то, что затем приводит к сохранению изменений, сделанных путем вызова .Save() и других методов изменения. Вполне возможно, что UoW каким-то образом отслеживает эти изменения.

person Chris Nicola    schedule 25.06.2011

arrow_upward
2
arrow_downward

  • UnitOfWork — ваш обработчик транзакций
  • Репозиторий выполняет актуальную работу по загрузке/сохранению объектов в хранилище данных.

Я использую определения, подобные:

IUnitOfWork { Commit(); }
IRepository { GetAll(); GetById(); Add(T item); Remove(T item); }

Я бы не хотел, чтобы UnitOfWork создавал SQL — эта логика была бы в вашем репозитории. Какое хранилище данных вы используете?

person Matt    schedule 29.06.2011