首页 > 解决方案 > Dapper 和 n 层应用程序(导航属性)

问题描述

我正在努力弄清楚我应该如何在我的应用程序中实现 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。但默认情况下也会映射导航。我意识到我可以用属性装饰并在映射上读取它们,但是当我用谷歌搜索时,我意识到也许我应该放弃 UnitOfWork 模式和存储库,使数据层“超级薄”并且只暴露连接. 然后服务层会用正确的 sql 调用 Dapper,就像我今天会做的那样,但是使用存储库。

我还将删除导航属性并自行获取每个实体,并将它们组合到 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);
}

这将生成大量查询(几乎像 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() 并检索订单、订单线和产品的图表。

我不知道哪个解决方案是“最佳实践”。我试图找到一个很好的开源项目,使用层和 dapper 来获得一些现实世界的使用,但没有成功。删除导航属性的方法也删除了服务层的一些目的,因为我在某种程度上将一些业务逻辑移动到 mvc 控制器。

我找不到如何前进的好习惯,请指教。

标签: c#dapper

解决方案


如果您使用的 RDBMS 支持 JSON,我建议将您需要插入的所有内容包装到 JSON 中,并通过一次调用将其发送到存储过程。只需一次调用,即可使用相同的技术返回相关对象的图形。工作单元,实际上是一个事务,将在存储过程本身中得到处理,这也是处理操作数据恕我直言的事务的正确位置。

这极大地有助于减少往返行程,但会以数据库上使用更多 CPU 为代价。这通常不是问题,除非您期望一个非常大的数量(= 超过每秒数千个并发查询。

我在这里写了很多关于这个的文章:

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

更具体地说,“复杂的自定义处理”示例完全显示了我提到的内容。


推荐阅读