如何使用Dapper从动态变更列的表中提取数据
问题描述
现有Tasks表和Codes表,初始结构如下:
Tasks表:
| ID | Taskname |
|---|---|
| 1 | 2D |
| 2 | 3D |
Codes表:
| ID | Codename | For2D | For3D |
|---|---|---|---|
| 1 | 2D | 1 | 0 |
| 2 | 3D | 0 | 1 |
用户可通过前端按钮在Tasks表添加新条目,对应Codes表需新增列,例如新增4D任务后,Codes表会新增For4D列。目前已能通过Dapper实现列添加操作,但无法确定用户会添加多少新任务,因此不知如何编写查询并使用Dapper提取表中数据返回至前端。现有如下DqcDto类,但无法适配新增列:
public class DqcDto { public string? DqcCode { get; set; } public string? DqcDescription { get; set; } public string Project { get; set; } public bool? IsFor3D { get; set; } public bool? IsFor2D { get; set; } }
解决方案
针对动态列的适配问题,给你三个可行方案:
1. 直接用Dynamic类型快速解决
Dapper支持返回dynamic类型,无需固定DTO,自动匹配所有查询列:
using (var conn = new SqlConnection(yourConnString)) { var sql = "SELECT Codename AS DqcCode, DqcDescription, Project, For2D, For3D, For4D FROM Codes"; var data = conn.Query<dynamic>(sql).ToList(); // 直接序列化data返回前端即可,前端能拿到所有列 }
优点是代码简单,无需修改结构;缺点是编译时无类型检查。
2. 动态构建DTO和查询语句
先从Tasks表获取所有任务名,动态生成查询字段和映射:
// 第一步:获取所有任务 var tasks = conn.Query<TaskDto>("SELECT Taskname FROM Tasks").ToList(); // 第二步:构建动态列的查询别名(比如For2D → IsFor2D) var taskColumns = tasks.Select(t => $"For{t.Taskname} AS IsFor{t.Taskname}").ToList(); // 第三步:拼接完整SQL var sql = $"SELECT Codename AS DqcCode, DqcDescription, Project, {string.Join(", ", taskColumns)} FROM Codes"; // 第四步:用ExpandoObject动态存储结果 var results = new List<ExpandoObject>(); using (var reader = conn.ExecuteReader(sql)) { while (reader.Read()) { var expando = new ExpandoObject(); var dict = (IDictionary<string, object>)expando; // 固定字段赋值 dict["DqcCode"] = reader["DqcCode"]; dict["DqcDescription"] = reader["DqcDescription"]; dict["Project"] = reader["Project"]; // 动态字段赋值 foreach (var task in tasks) { var colName = $"IsFor{task.Taskname}"; dict[colName] = Convert.ToBoolean(reader[colName]); } results.Add(expando); } }
这种方式既能保留类型语义,又能适配动态列,返回的ExpandoObject可直接序列化为JSON给前端。
3. 重构数据库结构(长期最优解)
动态新增列的设计违反数据库范式,建议改成关联表结构:
- 新增
CodeTaskRelations表:
| CodeID | TaskID | IsApplicable |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 2 | 1 |
- 新增任务时只需往
Tasks表加条目,无需修改Codes表结构 - 查询时通过关联获取数据:
var sql = @"SELECT c.Codename AS DqcCode, c.DqcDescription, c.Project, t.Taskname, r.IsApplicable FROM Codes c LEFT JOIN CodeTaskRelations r ON c.ID = r.CodeID LEFT JOIN Tasks t ON r.TaskID = t.ID"; var data = conn.Query(sql).ToList();
这种结构更易维护,扩展性更强,避免后续因动态列带来的各种问题。
内容的提问来源于stack exchange,提问作者AoLiGei
相关产品推荐
相关产品推荐

