Dapper 可直接映射 C# 9 record 类型,无需额外配置;字段名需与数据库列名一致或通过 SQL 别名、[Column] 特性对齐,支持位置 record 和不可变属性,泛型查询及高级功能均兼容。

Dapper 可以直接映射到 C# 9 的 record 类型,无需额外配置,只要字段名与数据库列名一致或通过标准方式对齐即可。record 本质仍是编译后生成的 class,Dapper 对它的支持和普通 class 完全相同。
字段名自动匹配最常用
Dapper 默认按属性名(PascalCase)匹配数据库列名(通常是 snake_case 或 PascalCase)。只要 record 的属性名和 SQL 查询返回的列名能对上,就能自动填充。
- 比如数据库列是
user_name,record 属性写成UserName,Dapper 不会自动转换下划线;此时需用 SQL 别名或[Column]特性 - 推荐写法:SQL 中用别名统一风格,例如
SELECT id AS Id, user_name AS UserName FROM users - 这样 record 定义可简洁为:
public record User(int Id, string UserName);
支持位置记录(positional record)
C# 9 的位置 record(带括号参数的写法)也能被 Dapper 正常映射,前提是构造函数参数名与列名一致。
- 例如:
public record Product(int Id, string Name, decimal Price); - Dapper 会尝试通过反射找到匹配参数名的 public 构造函数,并用查询结果逐个传入
- 注意:如果 record 含多个构造函数,Dapper 会选择参数最多、且所有参数名都能匹配列名的那个
不可变性不影响读取,但写入需注意
record 默认不可变(属性只有 init 或 get),这对 Dapper 查询完全无影响——Dapper 只负责把数据填进去,不修改对象状态。
- 插入/更新时若用匿名对象或新 record 实例传参(如
conn.Execute(sql, new { Id = 1, Name = "A" })),一切照常 - 若想用 record 实例直接作为参数(如
conn.Execute(sql, myRecord)),确保 record 属性有 public getter,Dapper 能通过反射读取值即可 - 不需要 setter,Dapper 不需要写入 record 属性,只读取
反射与泛型兼容性良好
record 在运行时就是普通 class,typeof(MyRecord).IsClass == true,所以 Dapper 的泛型方法 Query、QueryFirstOrDefault 都能无缝使用。
- 支持 Dapper 的所有高级功能:多映射、复杂类型嵌套、自定义类型处理器等
- 在 ORM 层做值对象建模时,record + Dapper 是轻量又语义清晰的组合:比如订单项、地址、货币金额等天然适合用 record 表达
- 搭配领域驱动设计(DDD)中的值对象场景,既保证相等性逻辑(
a == b按值判断),又不增加映射成本
基本上就这些。record 和 Dapper 结合不复杂但容易忽略细节——关键是理解 record 不是“特殊类型”,而是编译器帮你省去样板代码的 class,Dapper 照样认得它。










