Dapper如何映射到只读属性

来源:IPIPP.com作者:落伍者头衔:草根站长
导读:本期聚焦于小伙伴创作的《Dapper如何映射到只读属性》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Dapper如何映射到只读属性》有用,将其分享出去将是对创作者最好的鼓励。

Dapper作为轻量级的ORM框架,默认映射逻辑依赖属性的公共setter方法,当实体类中的属性被定义为只读属性,也就是只有getter没有setter时,直接使用Query方法查询数据会出现属性值为默认值的情况,无法完成正常映射。这种情况在需要定义不可变实体类时非常常见,下面介绍几种可行的解决方案。

Dapper如何映射到只读属性

只读属性无法映射的原因

Dapper内部默认使用System.Reflection相关的API来为实体属性赋值,对于没有setter的属性,反射无法找到对应的赋值入口,因此不会将查询结果中的字段值写入到只读属性中。比如下面这个实体类,其中的IdName是只读属性:

public class User
{
    public int Id { get; }
    public string Name { get; }

    public User(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

如果直接执行下面的查询代码,得到的User对象的IdName都会是默认值:

using Dapper;
using System.Data.SqlClient;

string connStr = "Server=127.0.0.1;Database=Test;Uid=sa;Pwd=123456";
using var conn = new SqlConnection(connStr);
var users = conn.Query<User>("SELECT Id, Name FROM User");

方案一:使用构造函数映射

Dapper支持通过构造函数来初始化实体对象,只要构造函数的参数名称和查询结果的字段名称一致,就可以自动匹配参数完成对象创建,此时只读属性可以在构造函数中赋值。这种方式是最推荐的,符合不可变实体的设计理念。

上面的User类已经定义了包含idname参数的构造函数,只需要确保查询的字段名和参数名一致即可,Dapper会自动匹配构造函数完成映射,不需要额外配置。如果构造函数的参数名和字段名不一致,可以通过别名的方式让字段名和参数名匹配:

// 查询时给字段起别名,和构造函数参数名匹配
var users = conn.Query<User>("SELECT Id AS id, Name AS name FROM User");

方案二:自定义类型映射

如果需要更灵活的映射逻辑,可以实现ICustomTypeMapper接口,自定义类型映射规则,手动为只读属性赋值。首先需要定义自定义映射类:

using Dapper;
using System;
using System.Collections.Generic;
using System.Reflection;

public class UserMapper : ICustomTypeMapper
{
    public object Map(Type type, IDataRecord row) where Type : class
    {
        // 从查询结果中获取字段值
        var id = row.GetInt32(row.GetOrdinal("Id"));
        var name = row.GetString(row.GetOrdinal("Name"));
        // 调用构造函数创建对象
        return new User(id, name);
    }
}

然后需要在查询前注册这个自定义映射:

// 注册自定义映射,指定User类型使用UserMapper
SqlMapper.SetTypeMap(typeof(User), new CustomTypeMapper<User>(new UserMapper()));
// 执行查询,此时会使用自定义映射规则
var users = conn.Query<User>("SELECT Id, Name FROM User");

方案三:使用动态类型中转

如果不想修改实体类也不想自定义映射,可以先将查询结果映射为动态类型,再手动创建实体对象赋值给只读属性。这种方式适合临时处理少量映射场景:

var dynamicUsers = conn.Query("SELECT Id, Name FROM User").ToList();
List<User> users = new List<User>();
foreach (var item in dynamicUsers)
{
    users.Add(new User(item.Id, item.Name));
}

不同方案对比

下面是三种方案的适用场景和优缺点对比:

方案优点缺点适用场景
构造函数映射实现简单,无需额外配置,符合不可变设计要求构造函数参数名和字段名一致,复杂场景不够灵活实体结构简单,构造函数参数和字段匹配的场景
自定义类型映射映射逻辑灵活,可处理复杂映射规则需要额外编写映射类,配置稍复杂映射规则复杂,需要自定义赋值逻辑的场景
动态类型中转无需修改实体和额外配置,临时处理方便需要手动遍历赋值,性能稍差,类型不安全临时少量映射,不想做额外配置的场景

注意事项

  • 使用构造函数映射时,如果实体类有多个构造函数,Dapper会尝试匹配参数最多的构造函数,尽量保证构造函数参数和查询字段一一对应。
  • 自定义类型映射时,需要注意IDataRecord获取字段值时的类型匹配,避免类型转换异常。
  • 如果只读属性是值类型,没有赋值的话会显示默认值,需要确认映射逻辑是否生效。

Dapperreadonly_propertyORM映射CSharp修改时间:2026-07-11 11:12:26

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。