Dapper如何映射到只读属性 Dapper Readonly Property映射

来源:这里教程网 时间:2026-02-21 17:33:19 作者:

Dapper 默认只映射 public set 的属性,遇到只读属性(比如

public string Name { get; }
private set
)时,不会自动赋值。但可以通过几种方式实现映射,关键是让 Dapper 知道“这个字段要写进去”,即使 setter 不公开。

用构造函数参数匹配字段名

Dapper 支持通过构造函数注入完成只读属性初始化。只要 SQL 查询列名和构造函数参数名完全一致(大小写敏感),Dapper 就会用该参数创建对象。

实体类需定义含参构造函数,参数名与数据库字段/别名严格对应 属性声明为只读(
get;
)或私有 setter(
get; private set;
)均可
查询时使用
Query<t>()</t>
,Dapper 自动绑定构造参数

示例:

public class Product
{
    public int Id { get; }
    public string Name { get; }
    public decimal Price { get; }
<pre class='brush:php;toolbar:false;'>public Product(int id, string name, decimal price)
{
    Id = id;
    Name = name;
    Price = price;
}

}

SQL 查询必须用别名对齐参数名:

SELECT Id, Name, Price FROM Products

启用非公共成员访问(.NET Core/.NET 5+)

Dapper 在较新版本中支持通过配置开启非公共属性写入。需在应用启动时设置:

调用
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
(可选,用于下划线转驼峰)
关键一步:设置
Dapper.SqlMapper.SetTypeMap
或启用
UseConstructorBinding = false
并配合反射权限
更直接的方式:在
Query<t>()</t>
前临时启用私有 setter 赋值

实际常用做法是添加一行初始化代码(仅需一次):

Dapper.SqlMapper.AddTypeMap(typeof(Product), 
    new CustomPropertyTypeMap(typeof(Product), 
        (type, columnName) => type.GetProperties()
            .FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase))));

用 Dapper.Contrib 的 [Write(false)] + 私有字段回填

如果你用的是

Dapper.Contrib
扩展包,它本身不支持只读属性,但可以反向操作:把值先写进私有字段,再由只读属性读取。

给类加
[Table("Products")]
特性
[ExplicitKey]
标识主键,其他字段保持
private set
查询仍用原生
Query<t>()</t>
,不走 Contrib 的
GetAsync
方法(它依赖 public set)

也就是说:Contrib 适合增删改,只读映射推荐回归原生 Dapper + 构造函数方案。

避免踩坑的细节提醒

字段名和属性名不一致时,SQL 中一定要用
AS
显式别名,否则构造函数绑定失败
若属性是
get; init;
(C# 9+),Dapper 默认支持,无需额外配置
异步方法如
QueryFirstOrDefaultAsync<t>()</t>
同样适用上述所有方式
不要依赖
[ReadOnly(true)]
这类自定义特性——Dapper 不识别它们

基本上就这些。构造函数方式最稳定,也最符合领域模型封装原则。

相关推荐