多表分页联查——EF Core方式和Dapper方式
1.PageResult
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Water.Infrastructure.Common.Utils
{/// <summary>/// 封装分页查询结果/// </summary>public class PageResult<T>{/// <summary>总记录数</summary>public long Total { get; set; }/// <summary>当前页数据集合(默认初始化空列表,避免空引用)</summary>public List<T> Records { get; set; } = new List<T>();/// <summary>无参构造函数</summary>public PageResult() { }/// <summary>带参构造函数(总记录数 + 数据集合)</summary>public PageResult(long total, List<T> records){Total = total;Records = records;}}
}
2.EF Core方式
2.1.controller
/// <summary>
/// 查询订单列表(含商品明细)
/// </summary>
/// <param name="queryDto"></param>
/// <returns></returns>
[HttpGet("list")]
public async Task<ActionResult<Result<PageResult<List<OrderListWithItemsVO>>>>> GetOrderListAsync([FromQuery] OrderQueryDto orderQueryDto)
{_logger.LogInformation("查询订单列表(含商品明细),状态id:{id},page:{page},pageSize:{pageSize}", orderQueryDto.Status,orderQueryDto.Current,orderQueryDto.Size);var result = await _orderService.GetOrderListAsync(orderQueryDto);return Ok(result);
}2.2.业务逻辑层接口
Task<Result<PageResult<OrderListWithItemsVO>>> GetOrderListAsync(OrderQueryDto orderQueryDto);2.3.业务逻辑层实现类
public async Task<Result<PageResult<OrderListWithItemsVO>>> GetOrderListAsync(OrderQueryDto orderQueryDto)
{//拿到当前登录用户idvar userId=_userContext.GetUserIdOrThrow();//将用户id作为参数去查询他的订单列表var domain=await _orderRepository.GetOrderListAsync(orderQueryDto.Current,orderQueryDto.Size,orderQueryDto.Status,userId);//映射为vovar voList = domain.Records.Select(order =>{var vo = _mapper.Map<OrderListWithItemsVO>(order);vo.StatusDesc = GetStatusDesc(vo.Status);vo.Items = _mapper.Map<List<OrderItemVo>>(order.OrderItems);vo.PayStatusDesc = GetPayStatusDesc(vo.PayStatus);return vo;}).ToList();var resultPage = new PageResult<OrderListWithItemsVO>{Total = voList.Count,Records = voList};return Result<PageResult<OrderListWithItemsVO>>.Success(resultPage);}/// <summary>
/// 转换订单状态
/// </summary>
/// <param name="status"></param>
/// <returns></returns>
private string GetStatusDesc(int status) => status switch
{1 => "待付款",2 => "待接单",3 => "已接单",4 => "派送中",5 => "已完成",6 => "已取消",7 => "退款",_ => "未知状态"
};
/// <summary>
/// 转换订单支付状态
/// </summary>
/// <param name="status"></param>
/// <returns></returns>
private string GetPayStatusDesc(int status) => status switch
{0 => "未支付",1 => "已支付",2 => "退款",_ => "未知状态"
};
2.4.数据访问层接口
Task<PageResult<Order>> GetOrderListAsync(int current, int size, int? status, long userId);2.5.数据访问层实现类
public async Task<PageResult<Water.Domain.Entities.Order>> GetOrderListAsync(int current, int size, int? status, long userId)
{IQueryable<Water.Infrastructure.Data.Entities.Order> query = _dbContext.Orders.Include(i=>i.OrderDetails).ThenInclude(t=>t.WaterSpec).Where(w => w.UserId == userId);if (status.HasValue){query = query.Where(w => w.Status == status);}// 1. 查询总记录数var total = await query.CountAsync();// 2. 分页查询数据var dataOrders = await query.Skip((current - 1) * size).Take(size).ToListAsync();// 3. 批量关联订单项(避免循环查库)var orderIds = dataOrders.Select(o => o.Id).ToList();var orderDetails = await _dbContext.OrderDetails.Include(od => od.WaterSpec).Where(od => orderIds.Contains(od.OrderId)).ToListAsync();foreach (var order in dataOrders){order.OrderDetails = orderDetails.Where(od => od.OrderId == order.Id).ToList();}// 4. 转换为领域实体var domainOrders = dataOrders.Select(dataOrder =>{var domainOrder = _mapper.Map<Water.Domain.Entities.Order>(dataOrder);// 将数据层 OrderDetail 转换为领域层 OrderItem,并赋值给 OrderItemsdomainOrder.OrderItems = _mapper.Map<List<Water.Domain.Entities.OrderItem>>(dataOrder.OrderDetails);return domainOrder;}).ToList();// 5. 封装分页结果return new PageResult<Water.Domain.Entities.Order>{Total = total,Records = domainOrders};}2.6.AutoMapper映射
数据库OrderDetail → [映射配置] → 领域层OrderItem → 填充到领域Order的OrderItems → [映射配置] → VO层OrderItemVo → 填充到VO的Items
3.Dapper方式
3.1.引包
3.2.appsettings
{"ConnectionStrings": {"DefaultConnection": "Server=.;Database=YourDatabase;User Id=sa;Password=YourPassword;TrustServerCertificate=True;"}
}3.3.数据访问层具体实现,其他不变
public class OrderRepository : IOrderRepository
{private readonly WaterDbContext _dbContext;private readonly IMapper _mapper;private readonly string _connectionString;public OrderRepository(WaterDbContext dbContext, IMapper mapper, IConfiguration configuration){_dbContext = dbContext;_mapper = mapper;_connectionString = configuration.GetConnectionString("DefaultConnection")?? throw new ArgumentNullException("连接字符串未配置");}public async Task<PageResult<Water.Domain.Entities.Order>> GetOrderListAsync(int current, int size, int? status, long userId)
{//Dapper方式// 直接创建数据库连接(使用完自动释放)using IDbConnection connection = new SqlConnection(_connectionString);connection.Open(); // 手动打开连接(Dapper会自动处理,但显式打开更直观)// 1. 查询订单主表(分页+条件)var orderSql = @"
SELECT Id, Number, Status, OrderTime, CheckoutTime, Amount, Consignee, Phone,UserId, AddressBookId, PayStatus, Remark, Address, UserName,EstimatedDeliveryTime, DeliveryAmount
FROM [Order]
WHERE UserId = @UserId
AND (@Status IS NULL OR Status = @Status)
ORDER BY OrderTime DESC
OFFSET (@Current - 1) * @Size ROWS FETCH NEXT @Size ROWS ONLY;
";var orderParams = new{UserId = userId,Status = status,Current = current,Size = size};// 直接映射为领域实体Ordervar orders = (await connection.QueryAsync<Water.Domain.Entities.Order>(orderSql, orderParams)).ToList();// 2. 查询总记录数var countSql = @"
SELECT COUNT(1) FROM [Order]
WHERE UserId = @UserId
AND (@Status IS NULL OR Status = @Status);
";int total = await connection.ExecuteScalarAsync<int>(countSql, orderParams);if (!orders.Any()){return new PageResult<Water.Domain.Entities.Order>{Total = total,Records = new List<Water.Domain.Entities.Order>()};}// 3. 查询订单明细(关联WaterSpec,映射为OrderItem领域实体)var orderIds = orders.Select(o => o.Id).ToList();var orderDetailSql = @"
SELECT od.Id, od.Name, od.Image, ws.SpecName, ws.Capacity, od.Number, ws.Price AS SpecPrice, od.Amount, od.OrderId,od.WaterInfoId,od.WaterSpecId
FROM OrderDetail od
JOIN WaterSpec ws ON od.WaterSpecId = ws.Id
WHERE od.OrderId IN @OrderIds;
";// 映射为领域实体OrderItemvar orderItems = (await connection.QueryAsync<OrderItem>(orderDetailSql, new { OrderIds = orderIds })).ToList();// 4. 关联订单与明细(填充Order的OrderItems属性)foreach (var order in orders){order.OrderItems = orderItems.Where(od => od.OrderId == order.Id).ToList();}// 5. 返回领域实体的分页结果return new PageResult<Water.Domain.Entities.Order>{Total = total,Records = orders};
}
}