使用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表结构

解决方案
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
相关产品推荐
相关产品推荐

