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

.NET Core中如何将存储过程返回结果映射到含嵌套集合的实体对象

实现方案指导

方案选择建议

你仅需要获取第一条地址的场景下,两种返回方式都可实现,优先推荐多结果集方案:

  • 无数据冗余:单结果集需要关联两张表查询,员工基础信息会存在重复冗余,多结果集分别返回对应数据更轻量化
  • 扩展性更强:后续如果需要返回该员工的全部地址,仅需调整存储过程去掉地址查询的TOP 1限制即可,上层代码改动极小

推荐实现:多结果集方案

1. 存储过程编写

CREATE PROCEDURE GetEmployeeDetails
    @employeeid INT
AS
BEGIN
    SET NOCOUNT ON;
    -- 第一个结果集:返回员工基础信息
    SELECT Name, 【其余Employee类对应字段】 
    FROM Employee 
    WHERE Id = @employeeid;
    -- 第二个结果集:返回第一条关联地址
    SELECT TOP 1 【Address类对应所有字段】 
    FROM Address 
    WHERE EmployeeId = @employeeid;
END

2. C# 映射代码调整

你原代码使用的ExecuteQueryFirstOrDefaultAsync仅能读取单结果集,无法获取地址数据,需要改用Dapper的QueryMultipleAsync读取多结果集:

public async Task<Employee> GetEmployeeDetails(int employeeid)
{
    var parameters = new DynamicParameters();
    parameters.Add("@employeeid", employeeid);

    using (IDbConnection con = await GetConnectionAsync(ConfigHandler.GetConnectionString(Constants.DefaultConnectionString)))
    {
        using var multiResult = await con.QueryMultipleAsync(
            "GetEmployeeDetails", 
            parameters, 
            commandType: CommandType.StoredProcedure
        );
        // 读取第一个结果集的员工信息
        var emp = await multiResult.ReadFirstOrDefaultAsync<Employee>();
        if (emp != null)
        {
            emp.Addr = new List<Address>();
            // 读取第二个结果集的地址信息
            var firstAddr = await multiResult.ReadFirstOrDefaultAsync<Address>();
            if (firstAddr != null)
            {
                emp.Addr.Add(firstAddr);
            }
        }
        // 无需手动调用con.Close(),using块会自动释放连接资源
        return emp;
    }
}

可选实现:单结果集方案

如果偏好单结果集实现,可以使用Dapper的多映射功能完成类匹配:

1. 存储过程编写

CREATE PROCEDURE GetEmployeeDetails
    @employeeid INT
AS
BEGIN
    SET NOCOUNT ON;
    SELECT TOP 1 
        e.Name, -- 其余Employee对应字段
        a.Id, a.Street, -- 其余Address对应字段
    FROM Employee e
    LEFT JOIN Address a ON e.Id = a.EmployeeId
    WHERE e.Id = @employeeid;
END

2. C# 映射代码

public async Task<Employee> GetEmployeeDetails(int employeeid)
{
    var parameters = new DynamicParameters();
    parameters.Add("@employeeid", employeeid);

    using (IDbConnection con = await GetConnectionAsync(ConfigHandler.GetConnectionString(Constants.DefaultConnectionString)))
    {
        var empResult = await con.QueryAsync<Employee, Address, Employee>(
            "GetEmployeeDetails",
            (emp, addr) =>
            {
                emp.Addr = new List<Address>();
                if(addr != null) emp.Addr.Add(addr);
                return emp;
            },
            parameters,
            splitOn: "Id", // 此处填Address表的主键字段名,Dapper会按该字段拆分两类的映射
            commandType: CommandType.StoredProcedure
        );
        return empResult.FirstOrDefault();
    }
}

注意事项

  • 存储过程返回的字段名需要和C#类的属性名一一对应,若字段名不匹配可以在SQL中使用AS别名对齐
  • 确认地址表的关联外键为EmployeeId,若实际名称不同需要同步调整SQL语句中的关联条件

内容的提问来源于stack exchange,提问作者Ang_Dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 20:06:04