You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Dapper多映射含复合主键的一对多表时遇异常

Dapper一对多查询问题解决方案

问题描述

现有两张表Car和Wheel,为一对多关系:

  • Car表主键为CarID
  • Wheel表复合主键为CarID和WheelIndex

使用以下Dapper代码查询关联车轮的车辆时,每辆车的Wheels列表仅包含一个车轮:

public class Car
{
    public int CarId { get; set; }
    public List<Wheel> Wheels { get; set; } = new List<Wheel>();
    // 其他属性
}

public class Wheel
{
    public int CarId { get; set; }
    public int WheelIndex { get; set; }
    // 其他属性
}

public static void Main(string[] args)
{
    var connectionstring = "{insert connection string}";
    var query = "SELECT C.CarID, WheelIndex FROM Car C JOIN Wheel W ON C.CarId = W.CarId";
    using (var connection = new MySqlConnection(connectionstring)){
        var cars = connection.Query<Car, Wheel, Car>(query, (car, wheel) =>
        {
            car.Wheels.Add(wheel);
            return car;
        }, splitOn: "WheelIndex");
    }
}

注:因特殊原因无法使用Entity Framework。

错误原因

Dapper的Query<TFirst, TSecond, TResult>方法会为查询结果的每一行创建一个Car实例,再将对应行的Wheel添加进去。也就是说,同一个CarId的每一行数据都会生成独立的Car对象,每个对象仅包含当前行的Wheel,最终结果集合里会有重复的Car,每个仅含单个车轮。

解决方案

需要对查询结果按CarId分组,合并相同车辆的Wheels列表,以下是两种常用实现方式:

方式1:使用Lookup手动分组

using (var connection = new MySqlConnection(connectionstring))
{
    // 获取所有Car与Wheel的映射对
    var carWheelPairs = connection.Query<Car, Wheel, (Car Car, Wheel Wheel)>(
        query,
        (car, wheel) => (car, wheel),
        splitOn: "WheelIndex"
    );

    // 按CarId分组,合并对应Wheels
    var cars = carWheelPairs
        .GroupBy(pair => pair.Car.CarId)
        .Select(group => 
        {
            var targetCar = group.First().Car;
            targetCar.Wheels = group.Select(pair => pair.Wheel).ToList();
            return targetCar;
        })
        .ToList();
}

方式2:用字典缓存避免重复创建Car实例

using (var connection = new MySqlConnection(connectionstring))
{
    var carCache = new Dictionary<int, Car>();

    var cars = connection.Query<Car, Wheel, Car>(
        query,
        (car, wheel) =>
        {
            // 检查缓存中是否已有该车辆,无则添加
            if (!carCache.TryGetValue(car.CarId, out var existingCar))
            {
                existingCar = car;
                carCache.Add(existingCar.CarId, existingCar);
            }
            // 向已有车辆的Wheels列表添加当前车轮
            existingCar.Wheels.Add(wheel);
            return existingCar;
        },
        splitOn: "WheelIndex"
    )
    // 去重,取缓存中的唯一车辆实例
    .Distinct()
    .ToList();
}

额外提示:查询语句中建议明确列出Car和Wheel表的所有需要映射的字段,避免因字段缺失导致的映射异常;如果Wheel包含其他属性,需同步在SELECT语句中包含。

内容的提问来源于stack exchange,提问作者Kiiiieeeeuuuw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:34:58