简体   繁体   English

Dapper如何设置只读属性?

[英]How can Dapper set a read-only property?

I noticed the following behavior related to Dapper and C# read-only auto-properties (introduced in C# 6).我注意到以下与 Dapper 和 C# 只读自动属性(在 C# 6 中介绍)相关的行为。

Dapper is able to map an instance of a class with a read-only property set, which according to my understanding of how read-only properties work should not be possible. Dapper 能够 map 一个带有只读属性集的 class 的实例,根据我对只读属性如何工作的理解,这应该是不可能的。

The following code demonstrates this in action:以下代码演示了这一点:

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;
            };
        }
    }
}

The commented out lines produce compile-time error CS0200 as expected.注释掉的行会按预期产生编译时错误CS0200

No database setup is needed, just a working SQL connection (I'm using Postgres).不需要数据库设置,只需一个有效的 SQL 连接(我使用的是 Postgres)。 The model returned from GetData has it's Id property set, if I try to manually create an instance of the Model class I can't set it's Id property as it's read-only.从 GetData 返回的 model 设置了 Id 属性,如果我尝试手动创建 Model class 的实例,我无法设置它的 Id 属性,因为它是只读的。

I was wondering how Dapper can set the property anyway?我想知道 Dapper 如何设置该属性? I tried following the code in the Dapper repo but it's above my level of comprehension.我尝试遵循 Dapper 存储库中的代码,但它超出了我的理解水平。

I managed to follow up-to SqlMapper.cs#L1530 which is where I think the magic is happening, but beyond that point it's just too hard to follow.我设法跟进了SqlMapper.cs#L1530 ,这是我认为魔法正在发生的地方,但超出这一点就很难跟进了。

If someone could explain in simple terms I would be grateful:)如果有人能简单地解释一下,我将不胜感激:)

Thank you.谢谢你。

Under the hood, your declaration:在幕后,您的声明:

public long Id { get; }

is actually compiled to the following C# code:实际上编译成如下C#代码:

[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly long <Id>k__BackingField;

public long Id
{
    [CompilerGenerated]
    get
    {
        return <Id>k__BackingField;
    }
}

With Reflection , you can do some pretty crazy things.使用反射,你可以做一些非常疯狂的事情。 And one of those things is to set the value of a field marked as readonly .其中之一就是设置标记为readonly的字段的值。 (This has some interesting implications, like being able to set string.Empty to whatever you want!) (这有一些有趣的含义,比如能够将string.Empty设置为任何你想要的!)

In Dapper's case, it's effectively doing the following, but with a lot more error checking:在 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM