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

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

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

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

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

О текущем проекте:

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

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

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

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

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

Ответ 1

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

Короткий вариант моего ответа на вопрос номер 1 - "нет". Вышеупомянутая ссылка обеспечивает то, что я считаю лучшим для вас.

Ответ 2

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

alt text

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

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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