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

Прочитав множество статей, я все еще не уверен в ответственности шаблона 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)


В нашем проекте мы используем репозиторий, чтобы вести себя точно так же, как набор сущностей, а 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

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

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

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

person Chris Nicola    schedule 25.06.2011

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

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

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

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

person Matt    schedule 29.06.2011