Является ли шаблон DTO plus UnitOfWork хорошим подходом для разработки DAL для веб-приложения?

Я реализую DAL, используя структуру сущностей. В нашем приложении у нас есть три уровня (DAL, бизнес-уровень и презентация). Это веб-приложение. Когда мы начали внедрять DAL, наша команда думала, что DAL должен иметь классы, методы которых получают ObjectContext, предоставленный сервисами на бизнес-уровне, и работают над ним. Обоснование этого решения заключается в том, что разные ObjectContexts видят разные состояния БД, поэтому некоторые операции могут быть отклонены из-за проблем с соответствием внешних ключей и других несоответствий.

Мы заметили, что генерация и распространение контекста объекта из уровня сервисов создает высокую связь между уровнями. Поэтому мы решили использовать DTO, отображаемые Automapper (а не неуправляемые объекты или объекты самоконтроля, утверждающие высокую связь, открывающие объекты верхним уровням и низкой эффективностью) и UnitOfWork. Итак, вот мои вопросы:

  1. Это правильный подход для разработки DAL веб-приложения? Зачем?
  2. Если вы ответили "да" на 1., как это согласовать концепцию DTO с шаблонами UnitOfWork?
  3. Если вы ответили "нет" на 1., какой может быть правильный подход к разработке DAL для веб-приложения?

Пожалуйста, если возможно, дайте библиографию, подтверждающую ваш ответ.

О текущем дизайне:

Приложение планировалось разработать на трех уровнях: презентация, бизнес и DAL. Бизнес уровень имеет как фасады, так и услуги

Существует интерфейс под названием ITransaction (только с двумя способами для удаления и сохранения изменений), видимый только в сервисах. Для управления транзакцией существует класс Transaction, расширяющий ObjectContext и ITransaction. Мы разработали это, имея в виду, что на бизнес-уровне мы не хотим, чтобы другие методы ObjectContext были доступны.

В DAL мы создали абстрактный репозиторий, используя два универсальных типа (один для сущности, а другой для связанного с ней DTO). Этот репозиторий имеет методы CRUD, реализованные универсальным образом, и два универсальных метода для сопоставления DTO и сущностей универсального репозитория с AutoMapper. Абстрактный конструктор репозитория принимает ITransaction в качестве аргумента и ожидает, что ITransaction будет ObjectContext, чтобы назначить его своему защищенному свойству ObjectContext.

Конкретные репозитории должны получать и возвращать только типы.net и DTO.

Теперь мы сталкиваемся с этой проблемой: универсальный метод для создания не генерирует временный или постоянный идентификатор для прикрепленных объектов (пока мы не используем SaveChanges()поэтому нарушая транзакционность мы хотим); это означает, что сервисные методы не могут использовать его для связывания DTO в BL)

4 ответа

Решение

Здесь происходит ряд вещей... Я предполагаю, что вы используете 3-уровневую архитектуру. Тем не менее, мне неясно, какие несколько дизайнерских решений вы приняли и каковы были мотивы их принятия. В целом, я бы сказал, что ваш ObjectContext не должен передаваться в ваших классах. Должен быть какой-то класс менеджера или репозитория, который обрабатывает управление соединением. Это решает проблему управления состоянием вашей БД. Я считаю, что шаблон репозитория очень хорошо работает здесь. Оттуда вы сможете довольно легко реализовать шаблон единицы работы, поскольку управление вашим соединением будет осуществляться в одном месте. Учитывая то, что я знаю о вашей архитектуре, я бы сказал, что вы должны использовать стратегию POCO. Использование POCO не тесно связывает вас с каким-либо поставщиком ORM. Преимущество состоит в том, что ваши POCO смогут взаимодействовать с вашим ObjectContext (возможно, через какой-то репозиторий), и это даст вам возможность отслеживать изменения. Опять же, оттуда вы сможете реализовать шаблон "единица работы" (транзакция), чтобы дать вам полный контроль над тем, как должна вести себя ваша бизнес-транзакция. Я считаю, что это невероятно полезная статья, объясняющая, как все это сочетается. Код содержит ошибки, но он точно иллюстрирует лучшие практики для типа архитектуры, которую вы описываете: репозиторий, спецификация и реализация единицы работы

Краткая версия моего ответа на вопрос № 1 - "нет". Приведенная выше ссылка дает вам лучший подход.

Я всегда верил, что код может объяснить вещи лучше, чем миры для программистов. И это особенно актуально для этой темы. Вот почему я предлагаю вам взглянуть на отличный пример приложения, в котором реализованы все ожидаемые вами концепции.

альтернативный текст

Проект называется http://www.sharparchitecture.net/, он сосредоточен вокруг MVC а также NHibernate, но вы можете использовать те же подходы, просто заменив NHibernate части с EF те, когда они вам нужны. Цель этого проекта - предоставить шаблон приложения со всеми лучшими практиками сообщества для создания веб-приложений.

Он охватывает все распространенные и большинство необычных тем при использовании ORM, управлении транзакциями, управлении зависимостями с помощью контейнеров IoC, использовании DTO и т. Д.

А вот пример приложения.

Я настаиваю на том, чтобы прочитать и попробовать это, это станет для вас настоящим испытанием, как и для меня.

Вы должны посмотреть, что вообще означает внедрение зависимостей и инверсия управления. Это дало бы возможность контролировать жизненный цикл ObjectContext "снаружи". Вы можете убедиться, что для каждого http-запроса используется только 1 экземпляр контекста объекта. Чтобы избежать управления зависимостями вручную, я бы рекомендовал использовать StructureMap в качестве контейнера.

Еще одна полезная (но довольно сложная и сложная задача) - это абстракция постоянства. Вместо того, чтобы использовать ObjectContext напрямую, вы бы использовали так называемый репозиторий, который отвечает за предоставление коллекции, как API для вашего хранилища данных. Это обеспечивает полезный шов, который можно использовать для переключения основного механизма хранения данных или для полной проверки постоянства для тестов.

Как уже предположил Джейсон - Вы должны также использовать POCO (простые старые объекты clr). Несмотря на то, что все еще будет неявная связь с структурой сущностей, о которой вы должны знать, это намного лучше, чем использование сгенерированных классов.

Вещи, которые вы не можете найти в другом месте достаточно быстро:

  1. Старайтесь избегать использования единицы работы. Ваша модель должна определять границы транзакций.
  2. Старайтесь избегать использования универсальных репозиториев (также обратите внимание на IQueryable).
  3. Необязательно спамить Ваш код с именем шаблона репозитория.

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

Я сосредоточусь на ваших текущих проблемах: если честно, я не думаю, что вы должны передавать свой ObjectContext. Я думаю, что это приведет к проблемам. Я предполагаю, что контроллер или бизнес-служба будут передавать ObjectContext/ITransaction в репозиторий. Как вы будете гарантировать, что ваш ObjectContext будет правильно утилизирован? Что происходит, когда вы используете вложенные транзакции? Что управляет откатами транзакций вниз по течению?

Я думаю, что ваша лучшая ставка заключается в том, чтобы дать больше определения тому, как вы ожидаете управлять транзакциями в вашей архитектуре. Использование TransactionScope в вашем контроллере / сервисе - хорошее начало, так как ObjectContext соблюдает его. Конечно, вам может потребоваться принять во внимание, что контроллеры / сервисы могут совершать вызовы другим контроллерам / сервисам, в которых есть транзакции. Чтобы разрешить сценарии, в которых вы хотите получить полный контроль над своими бизнес-транзакциями и последующими вызовами в базе данных, вам необходимо создать своего рода класс TransactionManager, который включает и обычно управляет транзакциями вверх и вниз по вашему стеку. Я обнаружил, что NCommon отлично справляется как с абстрагированием, так и с управлением транзакциями. Взгляните на классы UnitOfWorkScope и TransactionManager. Хотя я не согласен с подходом NCommon, заставляющим репозиторий полагаться на UnitOfWork, его можно легко изменить, если вы захотите.

Насколько ваша проблема persistantID идет, проверьте это

Другие вопросы по тегам