如何使用Dapper实现存储过程返回多表的一对多关系映射
Dapper存储过程多结果集一对多映射方案
写法问题分析
- 第一种
QueryAsync多映射写法不适用当前场景:
Dapper的多映射重载是为处理单条SELECT语句返回的JOIN扁平结果设计的,只能拆分同一结果集的单行数据到多个对象,无法跨两个独立结果集做关联,所以用这个方法处理你的存储过程返回的多结果集是完全错误的。另外你第一个查询把主键别名设为了SpecID,但实体类主键属性是ID,会导致主键映射失败。 - 第二种
QueryMultiple写法逻辑正确,可做性能优化:
你现在手动关联的思路是对的,只是循环中每次用Where过滤子项,数据量大时会有重复扫描的性能损耗,可以用ToLookup提前做分组优化。
正确实现方案
方案1:QueryMultiple 多结果集关联(推荐适配你当前存储过程)
首先修正第一个SQL的主键别名,和实体属性对应:
-- 把Spec.ID as SpecID改成Spec.ID as ID,匹配实体的ID属性 SELECT Spec.ID as ID, Spec.Def_Specification_GroupID, Spec.SpecTitle, Spec.HasItem, Spec.IsActive, SpecGroup.SpecGroup FROM Def_Specification Spec INNER JOIN Def_Specification_Group SpecGroup ON Spec.Def_Specification_GroupID = SpecGroup.ID WHERE Spec.IsActive = 1 SELECT ID, Def_SpecificationID, SpecificationTitle FROM Def_Specification_Items
优化后的C#代码:
List<DefSpecificationAndGroupAndItems> specification; using (var multi = await connection.QueryMultipleAsync("SP_DEF_SPECIFICATION_GET_ALL_BY_ACTIVE", new { Active = active }, commandType: CommandType.StoredProcedure)) { var specs = (await multi.ReadAsync<DefSpecificationAndGroupAndItems>()).ToList(); var specItems = (await multi.ReadAsync<DefSpecificationItems>()).ToList(); // 提前把所有子项按父ID分组,避免循环里重复过滤 var itemLookup = specItems.ToLookup(i => i.Def_SpecificationID); foreach (var spec in specs) { spec.DefSpecificationItems = itemLookup[spec.ID].ToList(); } specification = specs; }
方案2:多映射实现(需修改存储过程为单SELECT联查)
如果你要使用多映射,需要把存储过程改成单条SELECT返回JOIN后的扁平结果:
SELECT Spec.ID as ID, Spec.Def_Specification_GroupID, Spec.SpecTitle, Spec.HasItem, Spec.IsActive, SpecGroup.SpecGroup, Item.ID, Item.Def_SpecificationID, Item.SpecificationTitle FROM Def_Specification Spec INNER JOIN Def_Specification_Group SpecGroup ON Spec.Def_Specification_GroupID = SpecGroup.ID LEFT JOIN Def_Specification_Items Item ON Spec.ID = Item.Def_SpecificationID WHERE Spec.IsActive = 1
对应的C#多映射代码,用字典去重父实体:
var specDict = new Dictionary<int, DefSpecificationAndGroupAndItems>(); await connection.QueryAsync<DefSpecificationAndGroupAndItems, DefSpecificationItems, DefSpecificationAndGroupAndItems>( "你的新存储过程名", (spec, item) => { // 去重父实体,避免同一规格被多次实例化 if (!specDict.TryGetValue(spec.ID, out var existingSpec)) { existingSpec = spec; specDict.Add(existingSpec.ID, existingSpec); } if(item != null) existingSpec.DefSpecificationItems.Add(item); return existingSpec; }, new { Active = active }, commandType: CommandType.StoredProcedure, splitOn: "ID" // 第二个ID是子项表的ID,作为拆分字段 ); var specification = specDict.Values.ToList();
内容的提问来源于stack exchange,提问作者1 VeSSuZ 1
相关产品推荐
相关产品推荐

