多表分页联查——EF Core方式和Dapper方式

1.PageResult

cs 复制代码
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

cs 复制代码
/// <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.业务逻辑层接口

cs 复制代码
Task<Result<PageResult<OrderListWithItemsVO>>> GetOrderListAsync(OrderQueryDto orderQueryDto);

2.3.业务逻辑层实现类

cs 复制代码
public async Task<Result<PageResult<OrderListWithItemsVO>>> GetOrderListAsync(OrderQueryDto orderQueryDto)
{
    //拿到当前登录用户id
    var userId=_userContext.GetUserIdOrThrow();
    //将用户id作为参数去查询他的订单列表
    var domain=await _orderRepository.GetOrderListAsync(orderQueryDto.Current,orderQueryDto.Size,orderQueryDto.Status,userId);
    //映射为vo
    var 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.数据访问层接口

cs 复制代码
 Task<PageResult<Order>> GetOrderListAsync(int current, int size, int? status, long userId);

2.5.数据访问层实现类

cs 复制代码
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,并赋值给 OrderItems
        domainOrder.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

cs 复制代码
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=YourDatabase;User Id=sa;Password=YourPassword;TrustServerCertificate=True;"
  }
}

3.3.数据访问层具体实现,其他不变

cs 复制代码
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
    };
    // 直接映射为领域实体Order
    var 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;
";
    // 映射为领域实体OrderItem
    var 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
    };
}
}
相关推荐
Eiceblue15 小时前
通过 C# 将 HTML 转换为 RTF 富文本格式
开发语言·c#·html
IUGEI15 小时前
synchronized的工作机制是怎样的?深入解析synchronized底层原理
java·开发语言·后端·c#
czhc114007566318 小时前
C# 1124 接收
开发语言·c#
勿芮介18 小时前
[开发技术]在.NetCore中使用Elsa工作流-初学篇
.netcore
我是苏苏18 小时前
已解决:.NetCore控制台程序(WebAPI)闪退,没有报错日志定位不到信息
.netcore
时光追逐者19 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 62 期(2025年11.17-11.23)
c#·.net·.netcore
司铭鸿19 小时前
祖先关系的数学重构:从家谱到算法的思维跃迁
开发语言·数据结构·人工智能·算法·重构·c#·哈希算法
宝桥南山1 天前
.NET 10 - Blazor web assembly应用的一些诊断方式
microsoft·微软·c#·asp.net·.net·.netcore
m0_626535201 天前
代码分析
开发语言·c#