#c# #asp.net-mvc #entity-framework #architecture
#c# #asp.net-mvc #entity-framework #архитектура
Вопрос:
У меня есть вопрос по архитектуре. У меня есть проект DAL с классами poco (эквивалентные таблицы в базе данных), проект BLL и проект пользовательского интерфейса. Проект пользовательского интерфейса имеет ссылку на проект BLL, а проект BLL имеет ссылку на проект DAL.
Я хотел бы отобразить в проекте пользовательского интерфейса данные, например, из таблицы Product из базы данных. Должен ли я создать в классе проекта BLL такой же, как класс poco в проекте DAL, вернуть его в проект пользовательского интерфейса и отобразить его?
Итак, это мой класс poco в DAL (эквивалентная таблица в базе данных):
public class Product
{
public int ID {get; set; }
public string Name {get; set; }
public String Address {get; set; }
}
В BLL я создал бизнес-объект, аналогичный классу poco выше:
public class ProductBO
{
public int ID { get; set; }
public string Name { get; set; }
public String Address { get; set; }
}
В BLL у меня также есть метод, который получает продукты из DAL и сопоставляет их с бизнес-объектами — ProductBO:
public class ProductService
{
public List<ProductBO> GetAllProducts()
{
List<ProductBO> productsBO = new List<ProductBO>();
using (var context = NorthwindFactory.CreateContext())
{
List<Product> products = context.Product.ToList();
foreach (var product in products)
{
productsBO.Add(new ProductBO { ID = product.ID, Address = product.Address, Name = product.Name });
}
}
return productsBO;
}
}
И теперь в проекте пользовательского интерфейса в контроллере я вызываю службу из BLL, которая возвращает список, и в представлении я могу отображать данные с помощью бизнес-объекта ProductBO.
@model IEnumerable<WebApplication1.BLL.BusinessObjects.ProductBO>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Address)
</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
</tr>
}
</table>
Это правильный подход к архитектуре?
Комментарии:
1. Если бы это был я, я бы разделил объекты домена между DAL и BLL. Таким образом, вы уменьшаете количество дублирующегося кода. Для представлений, если потребуется, я создам модели представлений, с которыми они взаимодействуют, но эти модели представлений также используют объекты домена. Таким образом, вам также не нужно конвертировать между
Product
иProductBO
Ответ №1:
Ну, нет единственно правильного подхода. Но я стараюсь избегать создания набора классов DAL. Современные ORM позволяют работать с классами POCO. Да, есть некоторые ограничения (например, перечисления), но ИМХО не стоит создавать две копии каждого бизнес-объекта и сопоставлять их. Итак, я использую один объект POCO, который находится в сборке бизнес-логики. Entity Framework работает с этим объектом, сохраняет и загружает его из базы данных. Нет сопоставления.
Уровень представления отличается. Обычно у вас есть несколько представлений одного и того же объекта на разных страницах. Вы также должны использовать разные атрибуты аннотации данных для установки ограничений на модели представления или некоторые UIHints. Это загрязнило бы ваш бизнес-объект логикой, специфичной для пользовательского интерфейса. Также вам часто нужно будет отображать форматированные или измененные данные, например, полное имя вместо имени и фамилии вашего лица. сущность. Итак, здесь я не использую свои бизнес-объекты POCO, а вместо этого создаю модели представления.
С вашим образцом продукта этот подход будет выглядеть так:
- Сборка бизнес-логики имеет объект POCO
Product
- Сборка с сохранением ссылается на сборку бизнес-логики и использует то же самое
Product
- Проект пользовательского интерфейса имеет разные модели представления и т.д.
ProductViewModel
BriefProductViewModel
Он также отвечает за сопоставлениеProduct
и просмотр моделей. Примечание — ручное сопоставление занимает много времени. Я предлагаю вам использовать какую-нибудь библиотеку сопоставления, например AutoMapper или ValueInjecter
Комментарии:
1. Что-то должно быть не так в вашем объяснении. Я полагаю, что ваша сборка BL ссылается на «сборку сохранения», чтобы использовать ее, и вы также говорите, что ваша сборка сохранения ссылается на BL для использования
Product
. Это четкая циклическая ссылка. Как VS справляется с этим? (Должен быть какой-то скрытый ингредиент: DI), интерфейсы проекта wuth?) Я видел, что в ответе @Maarten используется та же техника. Интересно, как2. 1 за упоминание о том, что дополнительная модель «постоянства» обычно не стоит дополнительных усилий — ORM уже отчасти играют эту роль. Дополнительные уровни абстракции всегда являются компромиссами между гибкостью и сложностью / стоимостью. Также смотрите Эту статью: blog.ploeh.dk/2012/02/09/IsLayeringWorththeMapping
3. @JotaBe «Я полагаю, что ваша сборка BL ссылается на сборку persistence» => почему? BL не должен решать, как и когда сохраняются его объекты. Для этого не требуется знание уровня сохраняемости.
4. @guillaume31. Я пытаюсь понять, как это возможно. Например, если вы делаете заказ, используя BL «Order.New (некоторые данные)», бизнес-уровень должен где-то хранить это, поэтому он должен вызывать уровень постоянства, поэтому он должен иметь зависимость от этого уровня. Я понимаю, что BL не нужно знать, как, но BL решает, когда. Как BL может хранить данные, если он не ссылается на уровень сохранения ?. Если этот проект не определяет интерфейсы и не использует DI для реализации этих интерфейсов, я не знаю, как это возможно.
5. @JotaBe как я уже сказал в ответе, BL не ссылается на сборку сохранения. В нем есть сущности и службы, а также определение интерфейсов репозитория, которые реализованы в сборке сохранения. Службы BL определяют, когда использовать репозитории. Приложение ссылается на обе сборки — ASP.NET веб-сайт в данном случае. Реализация репозиториев, предоставляемых службам BL основным приложением. Вот почему BL ничего не знает о постоянстве
Ответ №2:
Вы должны определить свои объекты POCO (domain) в вашем проекте BLL, поскольку они являются бизнес-объектами.
Ваш DAL должен иметь ссылку на BLL, а не наоборот.
Лично я сторонник архитектуры Onion, которая помещает ваш домен в центр вашего приложения. Любой добавляемый вами слой должен ссылаться только внутрь, а не наружу. Итак, по этому определению ваш BLL является центром и ни на что не ссылается. Добавьте слой DAL, и он может ссылаться только внутрь, поэтому он может ссылаться на BLL. Уровень пользовательского интерфейса также ссылается только на BLL, но не на DAL, поскольку DAL является деталью реализации.
Комментарии:
1. Зачем DAL когда-либо нужна ссылка на BLL
2. Возможно, использовать объекты POCO, чтобы сохранить их в БД.
3. в примере OPs POCO находятся в DAL
4. Так, может быть, как сказал @3dd — я должен создать новый проект с классами poco, а проекты DAL, BLL, UI должны иметь ссылки на проект с классами poco?
5. Мой комментарий к другому ответу также применим к этому. Для кого-то, кто, вероятно, никогда не внедрял IoC, это должно выглядеть как «черная магия».