NHibernatedynamic列号
在我的C#项目中,我需要在代码中创build一个具有dynamic列号的表的数据库。 喜欢这个
------------------------------------------------------------------------------ | Time ¦ Element #1 | Element#2 ¦ ... ¦ Element#N ¦ ------------------------------------------------------------------------------ ¦ TimeValue#1 ¦ Element#1Value#1 ¦ Element#2Value#1 ¦ ... ¦ Element#NValue#1 ¦ ¦ TimeValue#2 ¦ Element#1Value#2 ¦ Element#2Value#2 ¦ ... ¦ Element#NValue#2 ¦ ... ¦ TimeValue#M ¦ Element#1Value#M ¦ Element#2Value#M ¦ ... ¦ Element#NValue#M ¦ ------------------------------------------------------------------------------
我使用简单的SQL查询“SQL CREATE TABLE”
public void AddTable(string TableName, Dictionary<string,Type> columns) { ... string query = @"CREATE TABLE " + TableName + "("; foreach (var c in columns) { SqlParameter temp = new SqlParameter(); string t = SetSQLType(ref temp, c.Value).ToString(); query += c.Key + " " + (t == SqlDbType.NVarChar.ToString() ? (t + "(200)") : (t)) + ","; } query = query.Substring(0, query.Length - 1); query += " )"; ... }
但是我想,也许,我可以使用更舒适的ORM,例如 – NHibernate。 我可以吗? 我阅读了关于按代码映射和dynamic组件的内容 ,但是我不确定在我的情况下究竟需要做什么。
我想,我需要这样的东西:
public class Elements { public virtual DateTime Timestamp { get; set; } public virtual IEnumerable<double> Values { get; set; } }
我可以映射这个类吗?
这个话题很有趣。 也许一开始看起来不太清楚。 以我的答案作为概述,下面列出的所有来源的总结。 也许它会给你答案。
从C#的angular度来看,你可以这样思考这些dynamic属性
public virtual IDictionary<keyType, valueType> Additional { get; set; } // (eg <string, object> public virtual IDictionary Additional { get; set; }
这两个都是dynamic的。 没有编译时检查处理IDictionary
。 更好的情况是检查IDictinary<,>
的通用参数。 但是因为我们正在谈论dynamic映射,编译时检查是我们可以牺牲的…
要将数据加载到这些字典之一中,我们必须(在大多数情况下)执行不同的映射,并具有不同的表结构。 generics对于转换包含在行中的数据来说非常方便。 非generics可用于列映射(如问题中的示例) 。 我们来讨论一下
1)genericsIDictionary<,>
– dynamic行
让我们从更安全的场景开始。 然后触摸接近问题的解决scheme
1a)三元协会
例如,让我们根据angular色/types将几个人映射到某个实体(例如Contract) 。 1)经理2)领导3)testing人员。 如果我们知道每个types/angular色只能有一个人(只有一个testing者或者没有),我们可以将其解释为:
public virtual IDictionary<PersonType, Person> Persons { get; set; }
我们现在是dynamic的。 可能有0,1或1+人与合同有关。 它们中的每一个都必须由PersonType
唯一。 我们还可以在运行时引入新的PersonType
,并扩展任何合同的相关人员集合…
映射应该是这样的
<map name="Persons" table="ContractPerson" > <key column="ContractId"/> <index-many-to-many column="PersonTypeId" class="PersonType"/> <one-to-many class="Person"/> </map>
这是6.9的一个例子。 三元协会 。 涉及到三个实体,我们仍然在运行时具有灵活性。 如前所述,我们可以插入新的PersonType并修改这些Contract-Person关系。
这个场景(与IDictiniary<string, object>
)仍然提供了大量的编译时间检查。
1b)接近问题
在上面描述的场景中,如果我们想要使用<map>
并且从行的angular度来看是dynamic的 – 我们需要一个像这样的表:
ElemntId| TheKey | TheValue (eg nvarchar) 1 | "name" | "Element A" 1 | "time" | "20:02" 1 | "date" | "2013-05-22" 1 | "value" | "11.22"
C#看起来像这样
public class Elements { ... public virtual IDictionary<string, string> Values { get; set; } }
制图:
<map name="Values" table="DynamicElementValues" > <key column="ElementId"/> <index column="TheKey" type="string"/> <element column="TheValue" type="string"/> </map>
因为我们已经使用了IDictionary<string, string>
所有的值都是string。 我们需要一些MetaData
来正确解释它的值
我们获得了:
- dynamic收集许多types的许多值
我们失去了:
- 能够将任何值放入SELECT或ORDER BY子句中
- 必须转换数据types(从一个string到其他)
1c)键作为一个string
实际上,
string
键字是dynamic的,但是太多了。 正如1a)所示,总是比较好的想法,以某种方式pipe理要用作关键字的一组值。 这就是为什么我们讨论了三元协会。 因为我们迟早不得不解释这本字典中的数据 – 用一些元数据,它可以方便地使用它们也作为一个关键…
2)非generics的IDictionary
– dynamic列
这次我们真的会尝试对列进行dynamic的解决scheme。
NHibernate的function,我们可以在这里使用的是:
- 4.4。 dynamic模型 ,
- 5.1.13。 组件,dynamic组件 ,
- 5.1.18。 join
这种映射与问题非常接近,符合我们的要求。 我们将有这个C#表示
public class Elements { ... public virtual IDictionary DynamicValues { get; set; } }
这可能是映射:
<join table="ElemntValues" > <key column="ElementId" /> <dynamic-component name="DynamicValues" > <property name="Time" type="TimeSpan" /> <property name="Date" type="DateTime" /> <property name="Salary" type="decimal" /> <property name="Color" type="string" /> <property name="WorkingDays" type="integer" /> <many-to one.... ... </dynamic-component> </join>
在这种情况下,我们确实有一个分表ElementValues
,它被join到我们的Parent实体(作为它的<class>
映射的一部分)。
这不是唯一的映射。 可能有其他映射types,例如4.4。 dynamic模型
<class entity-name="DynamicValues"...
这些可能需要一些更特殊的处理(插入,udpate,删除)
连接会简化很多东西,但总是会在SQL语句中使用(即使只需要父核属性)
它是dynamic的吗?
那么我们获得了:
- 在C#中,我们只有属性
IDictionary ElementValues
- NHibernate为我们运行时检查。 只有正确的值types可以插入到键(薪水必须是十进制)
- 有了一些
MetaData
模型,我们真的可以dynamic的给用户 - 任何Mapped属性都可以用在SELECT(投影)和ORDER BY(用户会喜欢它)
我们失去了:
- 一些性能,因为所有的数据(例如session.Get <>(id))将被加载
- 在添加或删除列的情况下,我们不是dynamic的。 所有映射都是应用程序分发的一部分, 不能在运行时更改。 那么,我们总是可以重新部署新的映射…
2b)IDictionary和MetaData
虽然
IDictinary
是一个非常dynamic的C#视图(它可以包含任何键/值对),但由于NHibernate映射,内容是被pipe理的。 只能将integer
数值添加到映射为integer
的属性中。 但是,我们如何在运行时知道:我们有什么密钥? 什么值可以放在那里,并从那里检索? 再次,我们需要一些MetaData …不是一个键的angular色,但他们将在运行时是至关重要的。 NHibernate检查是最后一道防线。
3)如何更改运行时的映射(添加新列)?
那么,文件中说明了什么? 7.5。 dynamic组件 :
这种映射的优点是只需通过编辑映射文档即可在部署时确定组件的实际属性。 (使用DOMparsing器,映射文档的运行时操作也是可能的。)
…使用DOMparsing器。 说实话,我不知道这是什么意思,如何实现。
还有Adam Bar的“按照代码进行映射 – dynamic组件 ”(见注释)
我认为dynamic组件在对象和数据库级别都不能真正dynamic化。 请注意,组件部分存储为普通列,所以我们需要知道它的列表…
但是从Firo有一个很好的想法- NHibernatedynamic映射 (看一下,对于一些提取过于复杂) 。 如果确实需要的话,这可能是一个真正的dynamic世界的解决scheme…运行时的新列… IDictionary
新键映射
概要
有了<map>
映射,我们可以非常有活力。 运行时不同的键和值。 无需重新部署。 但是我们不能在select或order中使用这些dynamic属性。 这些值很难过滤
对于一个<dynamic-component>
我们(开箱即可)依赖于映射。 但是我们在列中有我们的数据,因此我们可以使用连接来检索它们。 易于过滤,sorting。 和/但必须有一些metadata
来指导我们所拥有的和我们可以做的事情。
其他来源:
- Ayende dynamic组件
- Adam Bar, 按照代码映射 – dynamic组件
- Firo和运行时间映射replace
- 4.4。 dynamic模型
- 7.5。 dynamic组件