Путаница о том, где поставить бизнес-логику при использовании инфраструктуры Entity
Я только начал работать с инфраструктурой Entity, и я смущен тем, как классы, обычно входящие в бизнес-уровень, вписываются в объекты, созданные Entity Framework.
При работе с классическим ADO.NET у меня был бы класс под названием Customer, а затем еще один класс под названием DALCustomer для обработки взаимодействия с базами данных. В этой структуре я бы поставил код на выполнение вычислений, фильтрацию и обработку экземпляра DAL с клиентом для сохранения, обновления и удаления в классе Customer.
С помощью платформы Entity Framework, если у вас есть таблица Customer, инфраструктура Entity создает объект под названием Customer, и вот где начинается моя путаница, удаляет ли этот объект потребность в клиенте на бизнес-уровне? Итак, по существу все поля и методы, которые обычно входят в бизнес-уровень, входят в сущность, сгенерированную Entity Framework? Или, если класс еще существует в бизнес-слое CustomerBL, например, который по-прежнему содержит поля и методы, необходимые для выполнения бизнес-логики, необходимой для вычислений, фильтрации и все еще нуждается в экземпляре EF DAL, объявленном для обработки доступа к данным?
Если должен быть бизнес-класс, в данном случае CustomerBL, возникает еще один вопрос, если поля, созданные в объекте клиента, будут воссозданы в CustomerBL или должен быть объявлен экземпляр объекта Customer в CustomerBL, так что не было бы необходимости объявлять поля в двух местах?
Ответы
Ответ 1
Структура Entity, в отличие от linq-to-sql, например, была разработана с учетом разделения модели данных и концептуальной модели. Он поддерживает inheritance, разделение объектов, разбиение таблицы, сложные типы и прозрачные ассоциации "многие-ко-многим" , все из которых позволяют форматировать модель домена для удовлетворения потребностей, не слишком сильно ограничивая модель хранилища данных.
Первый подход кода позволяет работать с POCO, в котором выбранные свойства могут быть сопоставлены столбцам хранилища данных. Сначала Model-first и Database-first генерируют частичные классы, позволяя расширять сгенерированный код. Работа с этими классами в значительной степени связана с работой POCOs. Еще больше, начиная с версии 5, когда DbContext
стал стандартным API, поэтому сгенерированные классы больше не были набиты кодом, связанным с сохранением, в API ObjectContext
.
Конечно, это разделение концептуальной модели и модели магазина может только быть достигнуто в определенной степени. Некоторые вещи работают против этой цели сохранения невежества. Например, если требуется ленивая загрузка, необходимо объявить свойства навигации как virtual
, поэтому EF может переопределить их в прокси-типах. И очень удобно иметь примитивные свойства внешнего ключа (например, ParentId
), сопровождающие "реальные" ассоциации (a Parent
reference). Пуристы считают это нарушением правил, управляемых доменом.
Другим важным нарушением упорства в невежестве является большое количество различий между linq-to-objects и linq-to-entity. Вы просто не можете игнорировать тот факт, что вы связаны с совершенно другим универсумом, чем с объектами в памяти. Это называется плотная связь или нечеткая абстракция.
Но тогда... вообще, я доволен использованием сгенерированных классов EF или POCOs из первой модели кода в качестве классов домена. До сих пор я никогда не видел перехода на трение от одного хранилища данных к другому, если это вообще происходит. Сохранение невежества - это вымысел. Особенности DAL всегда оставляют след в области. Только когда вам нужно кодировать разные хранилища данных/модели или когда магазины/модели, как ожидается, будут меняться относительно часто, это окупается, чтобы максимально уменьшить этот след или полностью абстрагировать его.
Еще один фактор, который может способствовать классам EF в классах доменов, заключается в том, что многие приложения сегодня имеют несколько уровней, где (сериализованные) разные модели просмотра или DTO отправляются клиенту. Использование классов домена в пользовательских интерфейсах вряд ли когда-либо подходит для счета. Вы можете также использовать классы EF в качестве домена и предоставлять услуги для выделения выделенных моделей и DTO, как того требует пользовательский интерфейс или пользователи услуг. Другой слой абстракции может быть скорее бременем, чем благом, хотя бы с точки зрения производительности.
Ответ 2
По моему мнению, весь смысл использования POCOs в качестве объектов, которые могут быть сохранены, заключается в устранении различия между "сущностями базы данных" и "бизнес-субъектами". "Сущности" должны быть "бизнес-сущностями", которые непосредственно могут сохраняться и загружаться из хранилища данных и, следовательно, одновременно действовать как "сущности базы данных". Используя POCOs, бизнес-объекты отделены от конкретного механизма для взаимодействия с базой данных.
Вы можете переместить объекты в отдельный проект - например, - который не имеет ссылок на какую-либо сборку EF и все же использует их в проекте уровня базы данных для управления сохранением.
Это не означает, что вы можете полностью проектировать свои бизнес-объекты, не имея при этом требований к EF. Существуют ограничения, которые необходимо знать, чтобы избежать проблем, когда вы пришли к точке, чтобы сопоставить бизнес-объекты с базой данных с помощью EF, например:
- Вы должны сделать свойства навигации (ссылки или коллекции ссылок на другие объекты)
virtual
для поддержки ленивой загрузки с помощью EF
- Вы не можете использовать
IEnumerable<T>
для коллекций, которые необходимо сохранить. Он должен быть ICollection<T>
или более производным.
- Нелегко сохранить свойства
private
- Тип
char
не поддерживается EF, и вы не можете его использовать, если хотите сохранить его значения.
- и многое другое...
Но дополнительный набор объектов - на мой взгляд - дополнительный уровень сложности, который должен быть оправдан, чтобы быть действительно необходимым, если указанные ограничения слишком жесткие для вашего проекта.
YA2C (Еще 2 цента:))
Ответ 3
Я не знаю, считал ли он хорошую практику другими, но лично это так, как я справлялся с этим в прошлом:
Классы, созданные EF, являются вашим DAL, а затем для BL создают дополнительный набор классов, в которых у вас будет требуемая структура (например, возможно объединение данных из связанных объектов в отношениях один к одному) и другую бизнес-логику (пользовательская проверка, например, реализация IDataErrorInfo, чтобы она играла хорошо с пользовательским интерфейсом в WPF, например), а также создавать классы, которые будут содержать все методы бизнес-уровня, относящиеся к типу сущности, которые используют экземпляры BL и конвертируют в от объектов EF до объектов BL.
Итак, например, у вас есть Клиент в своем db. EF будет генерировать класс Customer, а в BL будет класс Customer (префикс, суффикс и т.д.) И класс CustomerLogic. В классе BL Customer вы можете делать все, что необходимо для удовлетворения требований, без необходимости вмешиваться в объекты EF, а в классе CustomerLogic у вас бы были методы BL (загрузка самых уважаемых клиентов, сохранение клиентов с дополнительными данными и т.д.).
Теперь это позволит вам быть слабо связанными с реализацией источника данных. Еще один пример того, почему это мне помогло в прошлом (в проекте WPF), состоит в том, что вы можете делать такие вещи, как реализовать IDataErrorInfo и реализовывать логику проверки в классах CustomerBL, чтобы при привязке объекта к форме создания/редактирования в пользовательском интерфейсе вы получите преимущества от встроенных функций, предоставляемых WPF.
... Мои 2 цента, мне также интересно узнать, что такое лучшая практика или какие другие решения/точки зрения.
Также, возможно, связанный с этим темой - Code-first vs Model/Database-first
Ответ 4
Этот вопрос может быть немного старым, но это может помочь. Андрас Немес отметил в своем блоге озабоченность использованием DDD (доменного дизайна) над технологическим дизайном, таким как EF, MVC и т.д.
http://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
Ответ 5
Я использовал бизнес-логику для написания своих методов и возврата результатов в созданном виде, например:
namespace Template.BusinessLogic
{
public interface IApplicantBusiness
{
List<Template.Model.ApplicantView> GetAllApplicants();
void InsertApplicant(Template.Model.ApplicantView applicant);
}
}