You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模仿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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 18:14:54