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

使用Dapper QueryAsync调用MySQL存储过程返回空列问题

Dapper调用MySQL存储过程返回实体部分字段为null的解决方案

问题背景

使用Dapper调用MySQL存储过程GetAllCourses返回Course实体列表时,仅Id字段有值,CourseName和HoleLayouts均为null。调试发现仓储层返回结果中CourseName即为null,已尝试修改course_name列长度但无效果,相同逻辑在其他实体上可正常运行。

相关代码与信息

实体类

public class Course
{
    public int Id { get; set; }
    public string CourseName { get; set; } = null!;
    public ICollection<HoleLayout> HoleLayouts { get; set; } = null!;
}

仓储查询代码

public async Task<IEnumerable<Course>> FindAllAsync()
{ 
    using var connection = _dataAccessor.CreateConnection();

    var courses = await connection.QueryAsync<Course>("GetAllCourses",
        commandType: CommandType.StoredProcedure);

    return courses;
}

存储过程

create
    definer = root@localhost procedure GetAllCourses()
BEGIN
    SELECT * FROM courses;
END;

JSON响应

[
    {
        "id": 1,
        "courseName": null,
        "holeLayouts": null
    }
]

Course表结构

Course Schema


解决方案

1. CourseName字段为null的修复

问题根源:数据库列名为course_name(下划线命名),实体属性为CourseName(帕斯卡命名),Dapper默认不会自动将下划线命名的列映射到帕斯卡/驼峰命名的属性,导致字段匹配失败。

可选修复方式:

  • 全局开启下划线映射:在项目启动代码(如Program.cs)中添加以下配置,让Dapper自动处理下划线到帕斯卡/驼峰的映射:
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
  • 修改存储过程加列别名:给查询结果的列指定与实体属性一致的别名:
create
    definer = root@localhost procedure GetAllCourses()
BEGIN
    SELECT id, course_name AS CourseName FROM courses;
END;
  • 实体属性加特性标注:给CourseName属性添加Column特性,指定对应数据库列名:
public class Course
{
    public int Id { get; set; }
    [Column("course_name")]
    public string CourseName { get; set; } = null!;
    public ICollection<HoleLayout> HoleLayouts { get; set; } = null!;
}

2. HoleLayouts字段为null的修复

问题根源:HoleLayouts是关联表的导航属性,当前查询仅从courses表获取数据,Dapper不会自动加载关联集合数据,需手动处理关联查询。

可选修复方式:

  • 分两次查询关联数据:先查课程列表,再批量查询对应布局数据并关联:
public async Task<IEnumerable<Course>> FindAllAsync()
{ 
    using var connection = _dataAccessor.CreateConnection();

    // 查询所有课程
    var courses = (await connection.QueryAsync<Course>("GetAllCourses", commandType: CommandType.StoredProcedure)).ToList();
    var courseIds = courses.Select(c => c.Id).ToList();
    if (!courseIds.Any()) return courses;

    // 查询对应所有布局数据
    var holeLayouts = await connection.QueryAsync<HoleLayout>(
        "SELECT * FROM hole_layouts WHERE course_id IN @CourseIds",
        new { CourseIds = courseIds });

    // 关联数据到对应课程
    foreach (var course in courses)
    {
        course.HoleLayouts = holeLayouts.Where(h => h.CourseId == course.Id).ToList();
    }

    return courses;
}
  • 多映射JOIN查询:通过JOIN查询一次性获取关联数据,并用Dapper多映射拆分结果集:
public async Task<IEnumerable<Course>> FindAllAsync()
{ 
    using var connection = _dataAccessor.CreateConnection();

    var sql = @"SELECT c.id, c.course_name, h.id as HoleId, h.layout_name 
                FROM courses c 
                LEFT JOIN hole_layouts h ON c.id = h.course_id";

    var courseDict = new Dictionary<int, Course>();
    await connection.QueryAsync<Course, HoleLayout, Course>(
        sql,
        (course, holeLayout) => 
        {
            if (!courseDict.TryGetValue(course.Id, out var existingCourse))
            {
                existingCourse = course;
                existingCourse.HoleLayouts = new List<HoleLayout>();
                courseDict.Add(existingCourse.Id, existingCourse);
            }
            if (holeLayout != null)
            {
                existingCourse.HoleLayouts.Add(holeLayout);
            }
            return existingCourse;
        },
        splitOn: "HoleId");

    return courseDict.Values;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 17:10:34