如何通过Dapper实现EntityA关联多个EntityB的正确数据映射?
解决Dapper一对多关联查询的集合填充问题
当使用Dapper查询一对多关联的实体时(比如一个EntityA对应多个EntityB),如果直接简单查询会导致每个EntityA只返回单个EntityB,这是因为Dapper默认不会自动将同属一个EntityA的多个EntityB合并到集合中。需要通过指定结果集拆分逻辑和自定义映射来解决。
1. 确保实体类结构正确
EntityA需要包含一个存储EntityB的集合属性,示例结构如下:
public class EntityA { public int Id { get; set; } public string Name { get; set; } // 用于存储关联的多个EntityB实例 public List<EntityB> EntityBs { get; set; } = new List<EntityB>(); } public class EntityB { public int Id { get; set; } public string Description { get; set; } } // 关联表实体(查询时会用到表结构,但映射时无需直接使用) public class LinkingEntity { public int EntityAId { get; set; } public int EntityBId { get; set; } }
2. 编写关联查询SQL语句
需要关联查询EntityA、关联表LinkingEntity和EntityB,并按EntityA的主键排序(确保同一EntityA的所有关联记录连续返回):
SELECT a.Id AS EntityAId, a.Name, b.Id AS EntityBId, b.Description FROM EntityA a JOIN LinkingEntity le ON a.Id = le.EntityAId JOIN EntityB b ON le.EntityBId = b.Id ORDER BY a.Id
3. 使用Dapper多映射实现集合填充
通过Dapper的Query<TFirst, TSecond, TResult>方法,结合字典去重和自定义映射逻辑,将多个EntityB合并到对应EntityA的集合中:
using (var connection = new SqlConnection("你的数据库连接字符串")) { var querySql = @" SELECT a.Id AS EntityAId, a.Name, b.Id AS EntityBId, b.Description FROM EntityA a JOIN LinkingEntity le ON a.Id = le.EntityAId JOIN EntityB b ON le.EntityBId = b.Id ORDER BY a.Id"; // 用字典存储已处理的EntityA,避免重复实例化 var entityAMap = new Dictionary<int, EntityA>(); connection.Query<EntityA, EntityB, EntityA>( querySql, (entityA, entityB) => { // 检查当前EntityA是否已存在于字典中 if (!entityAMap.TryGetValue(entityA.Id, out var existingA)) { existingA = entityA; entityAMap.Add(existingA.Id, existingA); } // 将当前EntityB添加到对应EntityA的集合中 existingA.EntityBs.Add(entityB); return existingA; }, splitOn: "EntityBId" // 指定结果集拆分点:从EntityBId开始属于EntityB的字段 ); // 最终结果为字典中的所有EntityA实例,每个都包含完整的EntityB集合 var result = entityAMap.Values.ToList(); }
核心要点说明
splitOn参数:必须指定拆分字段,该字段是结果集中属于第二个实体(EntityB)的第一个列名,Dapper会以此为界拆分结果集,分别映射到EntityA和EntityB。- 排序要求:SQL必须按
EntityA的主键排序,保证同一EntityA的所有关联记录连续返回,否则映射时可能出现集合填充不完整的情况。 - 字典去重:由于查询结果中每个
EntityA与EntityB的关联都会生成一条记录,用字典存储已处理的EntityA可以避免重复创建实例,同时将多个EntityB合并到同一个实例的集合中。
内容的提问来源于stack exchange,提问作者Stephen Cossgrove
相关产品推荐
相关产品推荐

