我有一个应用程序,我正在保存未知的结构化数据。所以,我们暂时假设数据库中有一个名为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])
)
并且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])
)
好的,现在让我们假设提供者(我不知道它提供的数据)有一个名为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);
}
}
因此,当提供程序向我发送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();
}
现在你可以看到,我从传递给我的对象的tableName
获得了Type
。在我们的示例Foo
中。此外,我正在收集要用反射插入的字段列表。然而,相处中的障碍是Dapper
需要将对象发送给它以获取参数值。现在,如果我不需要在类型上提供DataId
属性,我可以传递给我的对象,但是我需要DataId
属性,所以我可以正确地关联日志
Dapper
可以做其他事情来帮助?TypeBuilder
更直接?我以前做过,可以再做一次,但男人很痛苦。TypeBuilder
我会对你的例子感兴趣,因为你可能知道一种比我更有效的方法。如果您有一些想法,请举例说明。在上面的代码示例中,您看到我尝试传入Dictionary<string, object>
,但Dapper
不接受。我几乎知道它不会因为我已经查看了源代码,但我只是希望我错过了一些东西。
全部谢谢!
答案 0 :(得分:3)
Dapper中有一个BuiltIn类型来传递参数,这些属性只是已知的运行时:
查看班级DynamicParameters
。它用作:
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);
然后作为参数对象传递给Execute
方法。它通常与SP一起使用(因此您也可以将其用于读取输出值),但没有理由阻止它使用常规的SELECT / INSERT / UPDATE查询。
答案 1 :(得分:1)
我知道你问过小巧玲珑但是......
Sauce DB实际上很好地处理了这种情况。有几种方法可以做到。下面是一些说明它的示例代码。注意:您可以覆盖表名的查找方式,并且有一些方法可以让您根据需要检索已解析的表名。
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);
}
}