多表分页联查——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
    };
}
}
相关推荐
格兰芬多呼神护卫9 小时前
python实现Latex格式的公式转OMML并写入word
python·c#·word
chao18984410 小时前
C 文件操作全解速览
服务器·c语言·c#
月巴月巴白勺合鸟月半12 小时前
一个DevExpress的Docx文件处理的Bug的解决
c#·bug
.NET修仙日记13 小时前
第一章:从零开始构建你的第一个C#/.NET应用程序
c#·.net·.net core
csdn_aspnet17 小时前
.NETCore、.NET 7 和 RabbitMQ 的发布-订阅模式
rabbitmq·.netcore·.net7.
爱吃香蕉的阿豪17 小时前
深入理解 .NET Core 中的 IServiceScopeFactory:用法、场景与静态类依赖注入
.netcore
m5655bj20 小时前
如何使用 Python 转换 Excel 工作表到 PDF 文档
开发语言·c#·excel
技术支持者python,php21 小时前
SUB设备电子狗加密狗开发
c#
唐青枫1 天前
循环插入太慢?试试 C#.NET SqlBulkCopy,一次导入上百万数据
c#·.net