c# - Dapper 如何设置只读属性?
问题描述
我注意到以下与 Dapper 和 C#只读自动属性(在 C# 6 中引入)相关的行为。
Dapper 能够映射具有只读属性集的类的实例,根据我对只读属性如何工作的理解,这应该是不可能的。
以下代码实际演示了这一点:
using Dapper;
using Npgsql;
using System;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Model
{
public long Id { get; }
}
class Program
{
static async Task<Model> GetData()
{
using (var connection = new NpgsqlConnection("..."))
{
await connection.OpenAsync();
var sql = @"
SELECT 22 AS Id";
return await connection.QuerySingleAsync<Model>(sql);
}
}
static async Task Main(string[] args)
{
var model = await GetData();
Console.WriteLine(model.Id);
var model2 = new Model();
//model2.Id = 22;
var model3 = new Model
{
//Id = 22;
};
}
}
}
注释掉的行按预期产生编译时错误CS0200 。
不需要数据库设置,只需一个有效的 SQL 连接(我正在使用 Postgres)。从 GetData 返回的模型设置了 Id 属性,如果我尝试手动创建 Model 类的实例,我无法设置它的 Id 属性,因为它是只读的。
我想知道 Dapper 是如何设置属性的?我尝试遵循 Dapper repo 中的代码,但它超出了我的理解水平。
我设法跟进了SqlMapper.cs#L1530,这是我认为魔术正在发生的地方,但除此之外,它太难以遵循了。
如果有人能简单地解释一下,我将不胜感激:)
谢谢你。
解决方案
在引擎盖下,您的声明:
public long Id { get; }
实际上是编译成下面的C#代码:
[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly long <Id>k__BackingField;
public long Id
{
[CompilerGenerated]
get
{
return <Id>k__BackingField;
}
}
使用反射,你可以做一些非常疯狂的事情。其中一件事是设置标记为的字段的值readonly
。(这有一些有趣的含义,比如能够设置string.Empty
为你想要的任何东西!)
在 Dapper 的案例中,它有效地执行了以下操作,但进行了更多的错误检查:
Model m = <instance of your Model class>;
Type t = m.GetType();
string propertyName = "Id"; // read using Reflection
FieldInfo fi = t.GetField("<" + propertyName + ">k__BackingField",
BindingFlags.Instance | BindingFlags.NonPublic);
if (fi != null)
fi.SetValue(m, 22); // 22 = value read from DB
推荐阅读
- javascript - fs readFile 在控制台中工作,但不在服务器 Node.js 中
- python - 使用 Cucumber 和子进程从 Node js 调用时,循环在 python 中不起作用
- xpath - R:如何在具有相同 URL 的多个页面上抓取表格
- flutter - 键盘消失自动颤动
- composer-php - 如何在我的 Drupal 8 DDEV-Local 项目中使用 Drush 9(而不是 Drush 10)?
- python - 如何在 CSV 文件中创建新工作表
- excel - excel宏不删除空行。只删除数据但我想删除数据以及行
- javascript - 传单 - 奇怪的圆形地图
- javascript - Nuxt - 动态路由生成返回状态 400
- python - 应使用 python 将单列中的每 3 行分配给新的 3 列