Dapper и n-уровневое приложение (свойства навигации) - PullRequest
0 голосов
/ 27 ноября 2018

Я изо всех сил пытаюсь понять, как мне следует применять dapper в моем приложении.У меня есть n-уровневое приложение mvc, и у меня есть некоторый опыт работы с EF.Даже если я считаю, что EF - это хорошо, я не прошел обучение, чтобы упростить его и не бороться с производительностью.В новом проекте мы решили попробовать dapper, в основном, чтобы получить контроль над sql и, надеюсь, получить хорошую производительность.

Фон

Я создал многоуровневую аппликацию (ядро) с этими слоями Web - mvc
Сервис - Бизнес-уровень для обработки бизнес-логики Данные - доступ к данным для доступа к даннымсервер ms sql

Я пошел дальше и начал внедрять UnitOfWork и универсальные репозитории в уровне данных.

Обычная структура в базе данных будет иметь вид

Order
   ref to User
   ref to Address
   OrderLine
      ref to Product

И вВо многих случаях я хочу получить несколько заказов со всеми линиями и продуктами.

Итак, я сделал так, чтобы свойства навигации для моделей сущностей были такими же, как в EF, и заполняли их с помощью dapper, используя мультизапрос, или разбивали результат на разные сущности и отображали их на графике.

Проблема

Проблема, с которой я сталкиваюсь - это когда я делаю вставку.У меня есть sqlextension, которое сопоставляет свойства столбцам таблицы.Но навигация также будет отображаться по умолчанию.Я понимаю, что могу украсить атрибутами и читать их при отображении, но по мере того, как я Google, я осознаю, что, возможно, мне следует удалить шаблон UnitOfWork, а также репозиторий, делая слой данных «сверхтонким» и просто выставляя соединение,Тогда сервисный слой будет вызывать Dapper с правильным sql, что я и сделал бы сегодня, но с репозиториями.

Я бы также отбросил свойства навигации и выбрал каждый объект самостоятельно, и объединил их вViewModel.

Моя проблема с этим заключается в том, что если мы возьмем таблицу заказов выше, мне нужно будет сделать что-то вроде этого, чтобы получить полный список (обычно с постраничной страницей, также я удалил пользователя / адрес)

var listModel = new OrderListViewModel();
var orders = orderService.GetAll();
foreach(var order in orders) {
   var orderModel = new OrderViewModel();   // also map fields

   var orderLines = orderService.GetOrderLinesForOrder(order.OrderId);
   foreach(var orderline in orderLines) {
      var orderLineModel = new OrderLineViewModel();   // also map fields
      var product = productService.GetProduct(orderline.ProductId);
      orderLineModel.Product = new ProductViewModel();   // also map fields
      orderModel.OrderLines(orderLineModel);
   }

   listModel.Orders.Add(orderModel);
}

Это создаст ALOT запросов (почти как EF-ленивая загрузка).Таким образом, я мог бы сделать сопоставление

var orders = orderService.GetAll();
var orderLines = orderService.GetOrderLinesForOrders(orders.Select(o => o.OrderId).ToArray() );   // get all orderlines for all orders
var products = productService.GetProductsForOrderLines(orderLines.Select(p => p.OrderLineId).ToArray() );  // get all products for all orderlines

foreach(var order in orders) {
   var orderModel = new OrderViewModel();   // also map fields

   var orderLines = orderLines.Where( o => o.OrderId == order.OrderId );
   foreach(var orderline in orderLines) {
      var orderLineModel = new OrderLineViewModel();   // also map fields
      var product = products.First(p => p.ProductId == orderline.ProductId);
      orderLineModel.Product = new ProductViewModel();   // also map fields
      orderModel.OrderLines(orderLineModel);
   }

   listModel.Orders.Add(orderModel);
}

Это будет генерировать гораздо меньше запросов SQL и является оптимальным по производительности, я думаю.Я знаю, что может быть проблема с более чем 2100 (?) Параметрами, но я думаю, что это не будет проблемой в моем случае.Проблема в том, что многие из наших таблиц имеют разный статус и имеют много связей с другими таблицами.Мне бы приходилось все время выполнять множество этих запросов.

Когда я впервые делал репозиторий и навигацию, я делал бы это как

repo.Get<Order, OrderLines, Product, Order>(sqlThatWouldJoinAllTables);
// split and map the structure into order Entity and just return that

Таким образом, я мог бы просто вызвать orderService.GetAll () и получить график заказа, линий заказов и продуктов.

Я не знаю, какое из решений является «наилучшей практикой».Я пытался найти хороший проект с открытым исходным кодом, используя слои и щегольство, чтобы получить реальное использование в мире, но безуспешно.Подход к удалению навигационных свойств также устраняет некоторые цели сервисного уровня, поскольку я в некотором роде перенес часть бизнес-логики на контроллер mvc.

Я не могу найти хорошую практику, как мне двигаться вперед, пожалуйста, совет.

1 Ответ

0 голосов
/ 03 декабря 2018

Если используемая вами СУБД поддерживает JSON, я бы предложил обернуть все необходимое для вставки в JSON и отправить его в хранимую процедуру всего одним вызовом.Та же самая техника может использоваться, чтобы возвратить граф связанного объекта только одним вызовом.Unit-Of-Work, в действительности транзакция, будет позаботиться о самой хранимой процедуре, которая также является подходящим местом для транзакций, которые работают с данными IMHO.

Это очень сильно помогает сократить-трипы за счет большего количества процессоров, используемых в базе данных.Обычно это не проблема, если вы не ожидаете действительно большого числа (= более нескольких тысяч одновременных запросов в секунду.

Я много писал об этом здесь:

https://medium.com/dapper-net/one-to-many-mapping-with-dapper-55ae6a65cfd4

и, более конкретно, пример «Сложной пользовательской обработки» показывает именно то, что я упоминал.

...