Совершенный код №2. Реализация Unit Of Work

3 июля 2010 г.

Тема реализации паттерна Unit Of Work уже поднималась в комментариях к статье «Совершенный код. Разбор выпуска №1». После чего мне пришло множество писем с вопросами по этой теме. Одно из последних писем выношу на отдельное обсуждение.

Александр Стешенко:

Вопрос косвенно связан со статьей в твоем блоге «Domain-Driven Design: aggregation root».

В примерах бизнес сценариев ты используешь конструкцию:

using (unitOfWorkFactory.Create())
{
var account = new Account("email", "password");

account.AddRole(Role.Admin);

accountRepository.Save(account);
}

В общем с этой конструкцией все ясно, я также изучил набор реализаций UoW для .NET/Java инфраструктур - они все внешне сводятся к одному и тому же. Меня интересует, как реализован конкретно используемый тобой UoW, как с ним работают репозитории, что представляет из себя фабрика.

Я продолжил копаться и нашел то что в принципе полностью удовлетворяет меня на данный момент: http://github.com/riteshrao/ncommon/tree/v1.1/NCommon/src/Data

Внешне из сервиса оно выглядит примерно также как и у тебя в примерах. Внутренне существенное, что разделено UoW и Транзакционность. Unit of Work отвечает только за отслеживание изменений и Flush, когда за транзакцию отвечает отдельная компонента инфраструктуры. Объединяются они с помощью UnitOfWorkScope, что означает UoW + транзакция. Именно он и юзается в бизнес логике. Фабрика UoW скрыта внутри.

Итак, ваши примеры реализации Unit of Work, best practicies, их плюсы и минусы пишите в комментариях или мне на почту.

Как обычно я выложу лучшие советы со ссылкой на авторов и возможно своими дополнениями. Авторы решений, оставляйте, пожалуйста, свои контакты (сайты, блоги), чтобы я мог на вас ссылаться.

Мой пример реализации →

19 комментариев:

  1. Хотелось бы узнать мнения по поводу такой реализации Unit of Work: http://blogs.microsoft.co.il/blogs/gilf/archive/2010/06/21/revisiting-the-repository-and-unit-of-work-patterns-with-entity-framework.aspx

    сам я скептически отношусь к такой реализации

    ОтветитьУдалить
  2. У меня разрыв мозга, вижу слово "вопрос", но ни самого вопроса, ни знака "?" нет. Явная формулировка отсутствует.

    ОтветитьУдалить
  3. @Артур
    Выделил жирным шрифтом.

    ОтветитьУдалить
  4. Look at http://code.google.com/p/taijutsu/source/browse/

    ОтветитьУдалить
  5. @Wanderer
    А ты сам какую используешь?

    ОтветитьУдалить
  6. @Nikita Govorov
    Если можно вкраце достоинства этого подхода и недостатки. Используете ли вы его в своих проектах?

    ОтветитьУдалить
  7. @Александр

    на текущем проекте используем Entity Framework 3.5

    Используем что-то типа этого (накидал на скорую руку, возможно что-то не учел, но думаю все предельно просто и понятно):
    public interface IUnitOfWork : IDisposable
    {
    void Save();
    }

    public class UnitOfWork : IUnitOfWork
    {
    private readonly TransactionScope _transactionScope;
    private readonly ObjectContext _context;

    private bool needToAcceptChanges;

    protected UnitOfWork(ObjectContext context)
    {
    _context = context;
    _transactionScope = new TransactionScope();
    }

    public void Save()
    {
    _context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
    _transactionScope.Complete();
    needToAcceptChanges = true;
    }

    public void Dispose()
    {
    if (needToAcceptChanges)
    _context.AcceptAllChanges();

    _context.Dispose();
    _transactionScope.Dispose();

    GC.SuppressFinalize(this);
    }
    }

    Что касается той реализации UOF, к которой я скептически отношусь: мое мнение такое, что не нужно UOF использовать для операций чтения (такая возможность есть в вышеуказанной реализации). Для целей "только для чтения" я использую RepositoryHolder, в котором находятся "лениво"-создающиеся репозитарии. В моей реализации UOF используется только для выполнения операций записи в рамках одной транзакции.
    Буду рад любой критики =)

    ОтветитьУдалить
  8. в конструкторе UOF допустил опечатку (нужно public вместо protected)

    ОтветитьУдалить
  9. @Wanderer
    Т.е. твоя реализация в данном случае оборачивает ObjectContext из EF.

    Тут осталось не раскрыто как репозитории взаимодействуют с UoW?

    Было бы полезно увидеть пример использования для сценария сохранения и чтения.

    Еще интересно, можешь оценить стоимость перехода на другую ORM с такой реализацией?

    ОтветитьУдалить
  10. На почте уже несколько "ручных" реализаций, т.е. не использующих никакие ORM.

    Есть ли у кого-то еще опыт создания/использования такого подхода?

    ОтветитьУдалить
  11. @Александр

    обязательно отвечу как будет время :)

    ОтветитьУдалить
  12. @Роман
    Давай, я на следующей неделе уже свою версию выложу.

    ОтветитьУдалить
  13. А как быть с Lazy-loading в ASP.NET MVC. Например я передаю в модель сущность которая имеет связанные сущности, которые в свою очередь подгружаются по требованию. В вашем примере сессия будет закрыта и ничего не получится.

    ОтветитьУдалить
  14. @Denis

    А вы где эти данные подгружать хотите? M? V? C?

    ОтветитьУдалить
  15. @Denis

    Если вам надо подгружать данные во View, значит что-то не так с архитектурой системы.

    Последнее место, где можно подгружать данные - это Модель. View предназначена только для отображения подготовленных данных.

    ОтветитьУдалить
  16. Ну для View все так и есть, она не знает о ленивой загрузке работает напрямую с коллекцией связанных объектов, Модель сама как раз и подгружает эти объекты.

    ОтветитьУдалить
  17. @Denis

    Я видимо неправильно сказал. Все данные должны быть готовы для отображения (без каких-либо подгрузок или доп. обработок), когда модель передается во View.

    ОтветитьУдалить

Моя книга «Антихрупкость в IT»

Как достигать результатов в IT-проектах в условиях неопределённости. Подробнее...