Взаимодействие между единицей работы и шаблонами репозитория
После прочтения большого количества статей я до сих пор не уверен в отношении ответственности шаблона Unit of Work при взаимодействии с репозиториями.
Хранилища несут ответственность за загрузку и сохранение агрегированных корневых объектов, поэтому рассмотрим следующий пример кода:
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);
}
Код Commit Work Commit затем построил бы все необходимые SQL, чтобы отобразить объект обратно в БД?
Вопрос 2)
Если я добавлю агрегатный корень в Единицу работы, это единица работы, ответственная за сохранение корня и его дочерних элементов, или должна быть методом сохранения репозитория, добавить измененные объекты в Единицу работы? например
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();
}
Спасибо,
Джеймс
Ответы
Ответ 1
В нашем проекте мы используем репозиторий, чтобы вести себя так же, как коллекция Entities, а 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 позаботится об отслеживании изменений, в Rollback мы просто создаем новый контекст. Стойкость обрабатывается через корень и его детей. Один экземпляр UnitOfWork разделяется между всеми репозиториями.
Если мы не используем LinqToSql, мы реализуем наш собственный UnitOfWork, возможно, он вызывает веб-сервис или что-то в этом роде, в этом случае мы сами отслеживаем изменения в самих классах сущностей, используя класс EntityBase.
У нас есть репозиторий для каждого корня, но иногда дети одного корня используются как сами корни, поэтому нам часто нужно что-то вроде OrderLineRepository, потому что у нас есть пример использования в нашей системе: пользователь хочет выполнить поиск заказа линии.
Ответ 2
Как правило, мне нравится видеть, что UoW отслеживает изменения, которые сохраняются, напрямую вызывая IRepository.Save().
Я также предпочитаю, чтобы код UoW обрабатывался как аспект и вне взаимодействия с доменом. Это означает, что он либо обрабатывается некоторыми глобальными обработчиками команд, либо кодом веб-служб как часть завершения запроса. Другими словами, начало запроса открывает единицу работы, заканчивая ее закрытие. Таким образом, домен может не знать UoW и его реализацию.
Выполнение единицы работы - это то, что затем приводит к изменениям, вызванным вызовом методов .Save() и других методов изменения. Вполне возможно, что UoW - это то, что каким-то образом отслеживает эти изменения.
Ответ 3
- UnitOfWork - это ваша транзакция
Обработчик
- Репозиторий выполняет
acutal работать для загрузки/сохранения объектов в
хранилище данных
Я использую определения, похожие на:
IUnitOfWork { Commit(); }
IRepository { GetAll(); GetById(); Add(T item); Remove(T item); }
У меня не было бы UnitOfWork, создающего SQL - эта логика была бы в вашем репозитории. В каком хранилище данных вы используете?