使用Dapper多映射含复合主键的一对多表时遇异常
Dapper一对多查询问题解决方案
问题描述
现有两张表Car和Wheel,为一对多关系:
Car表主键为CarIDWheel表复合主键为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
相关产品推荐
相关产品推荐

