[英]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
属性,所以我可以正确地关联日志。
Dapper
do something else to help? Dapper
可以做其他事情来帮助? TypeBuilder
? TypeBuilder
更直接? I've done it before, and can do it again, but man it's a pain. 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. 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);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.