简体   繁体   English

在C#中为Dapper创建动态类型

[英]Create Dynamic Type in C# for Dapper

I have an application in which I'm saving unknown structured data. 我有一个应用程序,我正在保存未知的结构化数据。 So, let's assume for a moment there is a table in the database named Foo and it looks like this: 所以,让我们假设在数据库中有一个名为Foo的表,它看起来像这样:

CREATE TABLE [dbo].[Foo]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [DataId] INT NOT NULL, 
    [Bar] VARCHAR(50) NOT NULL, 
    CONSTRAINT [FK_Foo_Data] FOREIGN KEY ([DataId]) REFERENCES [Data]([Id])
)

And Foo is related to a table named Data which is going to store the provider who logged the data as well as the date and time it was logged, and let's say that table looks like this: Foo与一个名为Data的表相关,该表将存储记录数据的提供程序以及记录的日期和时间,让我们说该表如下所示:

CREATE TABLE [dbo].[Data]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [ProviderId] INT NOT NULL, 
    [RecordDateTime] DATETIME NOT NULL, 
    CONSTRAINT [FK_Data_Provider] FOREIGN KEY ([ProviderId]) REFERENCES [Provider]([Id])
)

Okay, now let's assume that the provider (which I have no knowledge of the data it's providing) has a class named Foo that looks like this: 好的,现在让我们假设提供者(我不知道它提供的数据)有一个名为Foo的类,如下所示:

[Serializable]
public class Foo : ISerializable
{
    private string bar;

    public string Bar
    {
        get { return bar; }
        set { bar = value; }
    }

    public Foo()
    {
        Bar = "Hello World!";
    }

    public Foo(SerializationInfo info, StreamingContext context)
    {
        this.bar = info.GetString("Bar");
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bar", bar);
    }
}

So, when the provider sends me an instance of Foo I'm going to interrogate it and determine which table in the database it should be inserted into, and let's assume that code block looks like this: 因此,当提供程序向我发送一个Foo实例时,我将询问它并确定应该插入数据库中的哪个表,并假设代码块如下所示:

this.connection.Open();

try
{
    var parameters = new
        {
            ProviderId = registeredProvidersIdBySession[providerKey.ToString()],
            RecordDateTime = DateTime.Now,
        };

    var id = connection.Query<int>("INSERT INTO Data (ProviderId, RecordDateTime) VALUES (@ProviderId, @RecordDateTime); SELECT CAST(SCOPE_IDENTITY() as INT)", parameters).Single();

    var t = data.GetType();
    var fields = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    var tableName = string.Format("[{0}]", t.Name);
    var fieldList = string.Join(", ", fields.Select(p => string.Format("[{0}]", p.Name)).ToArray());

    var valueList = fields.Select(p => string.Format("@{0}", p.Name)).ToList();
    valueList.Insert(0, "@DataId");

    var values = new Dictionary<string, object>();
    values.Add("@DataId", id);
    foreach (var propertyInfo in fields)
    {
        values.Add(string.Format("@{0}", propertyInfo.Name), propertyInfo.GetValue(data, null));
    }

    return connection.Execute(string.Format(
        "INSERT INTO {0} ({1}) VALUES ({2})",
            tableName,
            fieldList,
            string.Join(", ", valueList.ToArray())), values);
}
finally
{
    this.connection.Close();
}

Now as you can see, I'm getting the tableName from the Type of the object passed to me. 现在你可以看到,我从传递给我的对象的Type中获取tableName In our example that's Foo . 在我们的例子中,那是Foo Further, I'm gathering the list of fields to insert with reflection. 此外,我正在收集要用反射插入的字段列表。 However, the hitch in the get along is that Dapper requires an object be sent to it for the parameter values. 然而,相处的障碍是Dapper需要将一个对象发送给它以获取参数值。 Now, if I didn't need to provide the DataId property on the type I could just pass in the object that was given to me, but I need that DataId property so I can relate the log correctly. 现在,如果我不需要在类型上提供DataId属性,我可以传递给我的对象,但是我需要DataId属性,所以我可以正确地关联日志。

What am I Asking? 我在问什么?

  1. Am I going to have to create a dynamic type or can Dapper do something else to help? 我是否必须创建动态类型或者Dapper可以做其他事情来帮助?
  2. If I'm going to have to create a dynamic type, is there a more straight forward way than using the TypeBuilder ? 如果我将不得不创建一个动态类型,是否比使用TypeBuilder更直接? I've done it before, and can do it again, but man it's a pain. 我以前做过,可以再做一次,但是男人很痛苦。
  3. If I'm going to have to use the TypeBuilder I'd be interested in your example because you may know of a more efficient way than I do. 如果我将不得不使用TypeBuilder我会对你的例子感兴趣,因为你可能知道比我更有效的方法。 So please include an example if you have some thoughts. 如果你有一些想法,请举一个例子。

DISCLAIMER 免责声明

In the code example above you see I tried passing in a Dictionary<string, object> instead but Dapper doesn't accept that. 在上面的代码示例中,您看到我尝试传入Dictionary<string, object>而不是Dapper不接受。 I pretty much knew it wouldn't because I'd already looked at the source code, but I was just hoping I missed something. 我几乎知道它不会因为我已经查看了源代码,但我只是希望我错过了一些东西。

Thanks all! 谢谢大家!

There is a BuiltIn type in Dapper to pass parameter which properties are known runtime only: check the class DynamicParameters . 在Dapper中有一个BuiltIn类型来传递参数,这些属性仅在运行时已知:检查类DynamicParameters It is used like: 它用作:

var p = new DynamicParameters();
p.Add("@a", 11);
p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

and then passed as a parameter object to the Execute method. 然后作为参数对象传递给Execute方法。 It is typically used with SP ( so you can use it also for reading the output values) but there are no reasons preventing it to work with regular SELECT/INSERT/UPDATE queries. 它通常与SP一起使用(因此您也可以将其用于读取输出值),但没有理由阻止它使用常规的SELECT / INSERT / UPDATE查询。

I know you asked about dapper but... 我知道你问过小巧玲珑但是......

Sauce DB actually handles this type of scenario pretty well. Sauce DB实际上很好地处理了这种情况。 There are a few ways you can do it. 有几种方法可以做到。 Below is some example code that illustrates it. 下面是一些说明它的示例代码。 Note: you can override how the table name is found and there are method that allow you to retrieve the resolved table name if you wish. 注意:您可以覆盖表名的查找方式,并且有一些方法可以让您根据需要检索已解析的表名。

Disclamer: I wrote SauceDB Disclamer:我写了SauceDB

public class Foo
{
    public int ID { get; set; }
    publi string Name { get; set; }
}

public class Bar
{
    public int ID { get; set; }
    publi string Name { get; set; }
}

public class  MainClass
{
    IDataStore _dstore;
    public void Main()
    {
        _dstore = new SqlServerDataStore("my_connection_string")//create data store

        InsertObject(new Foo());
        InsertObject(new Bar());
    }

    public void InsertObject(object o)
    {
        //this will assume the table it belongs to is the type name + an s
            //you can override the table name behavior as well
        _dstore.InsertObject(o);
    }
}

http://sauce.codeplex.com/ http://sauce.codeplex.com/

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

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