Dapper .NET Core中一对多关联映射失败问题排查
Dapper一对多关联映射解决方法
1. 模型类定义(关键:提前初始化集合避免null)
确保EnrollableClass里的关联集合在构造时就初始化,从根源避免null异常:
public class EnrollableClass { public int Id { get; set; } public string ClassName { get; set; } // 提前初始化集合,防止后续添加元素时出现空引用 public List<ClassUnit> ClassUnits { get; set; } = new List<ClassUnit>(); } public class ClassUnit { public int UnitId { get; set; } // 这个字段将作为Dapper拆分映射的标识 public int EnrollableClassId { get; set; } public string UnitName { get; set; } }
2. 调整SQL查询语句
必须保证查询结果包含两个模型的所有字段,且**ClassUnit的主键(如UnitId)要出现在结果集中**——这是Dapper区分两个对象的核心依据:
SELECT ec.Id, ec.ClassName, cu.UnitId, cu.EnrollableClassId, cu.UnitName FROM EnrollableClass ec LEFT JOIN ClassUnit cu ON ec.Id = cu.EnrollableClassId -- 这里可以加你的查询条件,比如 WHERE ec.Id = @ClassId
3. Dapper QueryAsync正确用法(手动分组合并关联集合)
Dapper默认会把每条数据库记录拆分为一组对象,需要手动将同一EnrollableClass的ClassUnit合并到对应集合中:
var sql = "上面的SQL语句"; var queryParams = new { ClassId = 1 }; // 替换为你的实际查询参数 var classDict = new Dictionary<int, EnrollableClass>(); await connection.QueryAsync<EnrollableClass, ClassUnit, EnrollableClass>( sql, (enrollClass, unit) => { // 用EnrollableClass的主键做字典键,实现分组 if (!classDict.TryGetValue(enrollClass.Id, out var existingClass)) { existingClass = enrollClass; classDict.Add(existingClass.Id, existingClass); } // 处理LEFT JOIN返回null的情况,避免空引用 if (unit != null) { existingClass.ClassUnits.Add(unit); } return existingClass; }, param: queryParams, splitOn: "UnitId" // 必须指定ClassUnit的第一个字段(主键),让Dapper知道在哪里拆分两个对象 ); // 最终结果就是字典中已关联好ClassUnits的EnrollableClass集合 var result = classDict.Values.ToList();
4. 常见问题排查
- splitOn参数错误:必须填
ClassUnit在SQL结果里的第一个字段(通常是主键),不能填外键EnrollableClassId。 - 字段名不匹配:如果模型属性名和数据库字段名不一致,要么在SQL里加别名(比如
cu.UnitName AS UnitTitle对应模型的UnitTitle),要么给模型属性加[Column("UnitName")]特性。 - 未处理null的Unit:LEFT JOIN会导致部分
EnrollableClass没有关联的ClassUnit,此时map函数里必须判断unit != null再添加,否则会抛出空引用异常。 - 集合未初始化:如果模型里的
ClassUnits没提前初始化,就算Dapper映射成功,添加元素时也会因为集合为null报错。
内容的提问来源于stack exchange,提问作者Josiah Hooten
相关产品推荐
相关产品推荐

