模仿EF Core的Include用法使用Dapper返回结果不符合预期
问题描述
我尝试用Dapper实现类似EF Core中.Include()的功能,获取包含关联ProductAttributeValues数据的ProductAttribute对象。但当前返回的结果和SQL查询的每行数据一一对应,每个ProductAttribute里只包含一个ProductAttributeValues项;而预期是返回2个ProductAttribute对象,其中一个包含3个关联的ProductAttributeValues。
实体类
public record ProductAttribute { public int Id { get; set; } public string Name { get; set; } public string? Link { get; set; } public AttributeType Type { get; set; } public List<ProductAttributeValues> AttributeValues { get; set; } = new(); } public record ProductAttributeValues { public int Id { get; set; } public required string Name { get; set; } public string? value { get; set; } public int ProductAttributeId { get; set; } public ProductAttribute ProductAttribute { get; set; } = new(); }
执行的SQL语句
select pa.Id,pa.Name,pa.link,pa.Type,pav.Id as AttributeId,pav.Name,pav.value,pav.ProductAttributeId from productattributes pa inner join productattributevalues pav on pav.ProductattributeId = pa.Id
当前使用的C#代码
var attributeResult = await connection.QueryAsync <ProductAttribute, ProductAttributeValues, ProductAttribute>(sql, (attributes, attributevalues) => { attributes.AttributeValues.Add(attributevalues); return attributes; }, splitOn: "AttributeId");
解决方案
Dapper默认会为SQL返回的每一行生成一个映射对象,因此需要手动处理分组逻辑,将同一ProductAttribute对应的关联值合并到集合中,以下是两种可行方案:
方案一:LINQ手动分组
先获取原始查询结果,再通过LINQ按主实体ID分组合并:
var rawResult = await connection.QueryAsync <ProductAttribute, ProductAttributeValues, (ProductAttribute Attr, ProductAttributeValues AttrValue)>( sql, (attr, attrValue) => (attr, attrValue), splitOn: "AttributeId" ); var attributeResult = rawResult .GroupBy(item => item.Attr.Id) .Select(g => { var attribute = g.First().Attr; attribute.AttributeValues = g.Select(item => item.AttrValue).ToList(); return attribute; }) .ToList();
方案二:字典跟踪分组(推荐)
使用字典记录已创建的主实体,避免重复实例化,同时合并关联项:
var attributeDict = new Dictionary<int, ProductAttribute>(); var attributeResult = await connection.QueryAsync<ProductAttribute, ProductAttributeValues, ProductAttribute>( sql, (attr, attrValue) => { if (!attributeDict.TryGetValue(attr.Id, out var existingAttr)) { existingAttr = attr; attributeDict.Add(existingAttr.Id, existingAttr); } existingAttr.AttributeValues.Add(attrValue); return existingAttr; }, splitOn: "AttributeId" ); attributeResult = attributeDict.Values.ToList();
说明
两种方案核心都是基于ProductAttribute.Id进行分组,将同一主实体对应的所有ProductAttributeValues合并到其AttributeValues集合中,最终得到符合预期的实体结构。
内容的提问来源于stack exchange,提问作者Ali Jebali
相关产品推荐
相关产品推荐

