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

Я реализую 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 ]

6 голосов
/ 05 января 2011

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

Краткая версия моего ответа на вопрос № 1:«нет».Приведенная выше ссылка обеспечивает, как мне кажется, лучший подход для вас.

4 голосов
/ 12 января 2011

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

alt text

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

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

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

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

2 голосов
/ 05 января 2011

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

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

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

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

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

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

1 голос
/ 06 января 2011

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

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

Что касается проблемы с persistantID, проверьте это

...