一尘不染

NHibernate动态列数

c#

在我的C#项目中,我需要用一个动态表号的表在代码中创建一个数据库。像这样

------------------------------------------------------------------------------     
| 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。我可以吗?我阅读了有关按代码映射和动态组件的信息,但是我不确定在我的情况下到底需要做什么。

我想,我需要这样的东西:

public class Elements
{
    public virtual DateTime Timestamp { get; set; }
    public virtual IEnumerable<double> Values { get; set; }
}

我可以映射此类吗?


阅读 291

收藏
2020-05-19

共1个答案

一尘不染

这个话题很有趣。乍一看可能不太清楚。将我的答案作为概述,从下面列出的所有来源中进行总结。也许它将给您答案。

从C#的角度来看,您可以通过以下方式考虑这些 动态 属性

public virtual IDictionary<keyType, valueType> Additional { get; set; } 
                 // (e.g. <string, object>

public virtual IDictionary Additional { get; set; }

这两个都是动态的。没有用于处理的编译时检查IDictionary。更好的情况是检查的通用参数IDictinary<,>。但是因为我们在谈论 动态
映射,所以可以牺牲编译时间检查…

要将数据加载到这些 词典 之一中,我们必须(在大多数情况下)进行不同的映射并具有不同的表结构。对于包含 在行中
的数据的转位,泛型将很方便。非泛型可用于 映射 (例如问题中的示例) 。让我们一起讨论

1)通用IDictionary<,>-动态行

让我们从更安全的类型开始。然后触摸问题旁边的解决方案

1a)三元协会

例如,让我们根据角色/类型将一些人员映射到某个实体 (例如合同)
。1)经理2)组长3)测试员。如果我们知道每种类型/角色只能有一个人(只有一个测试者或没有一个),我们可以将其解释为:

public virtual IDictionary<PersonType, Person> Persons { get; set; }

我们现在充满活力。合同可能有0、1或1+个人。它们每个都必须是唯一的PersonType。我们还可以PersonType在运行时中引入new
,并扩展任何Contract的相关Person集。

映射应该是这样的

<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>并且从 行的 角度来看是动态的-我们需要一个这样的表:

ElemntId| TheKey       | TheValue (e.g. 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>所有值都是字符串。我们需要一些MetaData来正确解释其 价值

我们获得了:

  • 动态收集许多类型的许多值

我们失去了:

  • 将任何值放入SELECT或ORDER BY子句的能力
  • 必须转换数据类型(从字符串转换为其他字符串)

1c)键为 string

实际上,带string键的字典是动态的,但是太多了。如1a)所示,最好以某种方式管理用作
的值集。这就是我们讨论三元关联的原因。由于我们迟早必须解释此词典中的数据-使用一些MetaData,因此也可以方便地将它们用作键…

2)非通用IDictionary- 动态

这次,我们将真正尝试对列进行动态解决方案。

NHibernate功能,我们可以在这里使用:

这种映射非常接近问题,符合我们的要求。我们将使用此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>

在这种情况下,我们确实已经将table ElementValues与table 联接到了我们的Parent实体(作为其<class>映射的一部分)。

这不是唯一的映射。可能还有其他映射类型,例如4.4。动态模型

<class entity-name="DynamicValues"...

这些可能需要更多特殊处理(插入,udpate,删除)

联接将简化很多工作,但始终会在SQL语句中使用(即使仅需要 父级 核心属性也是如此)

它是动态的吗?

好吧,我们获得了:

  • 在C#上,我们只有属性 IDictionary ElementValues
  • NHibernate为我们做运行时检查。只能将正确类型的值插入键(薪水必须为十进制)
  • 使用某些MetaData模型,我们可以真正为用户动态
  • 可以在SELECT(投影)和ORDER BY(用户喜欢)中使用任何Mapped属性。

我们失去了:

  • 一些性能,因为所有数据(例如session.Get <>(id))将被加载
  • 如果添加或删除列,我们 不是 动态的。所有映射都是应用程序分发的一部分,并且 不能 在运行时更改。好吧,我们总是可以重新部署新的映射…

2b)词典和元数据

尽管IDictinary从C#角度来看,它非常动态(它可以包含任何键/对值),但是由于NHibernate映射,所以对内容进行了管理。只能将integer值添加到映射为的属性中
integer。但是我们如何在运行时中知道:我们拥有哪些键?什么值可以放在那里并从那里检索?同样,我们需要一些MetaData
…不是充当键的角色,但是它们在运行时中至关重要。NHibernate检查是最后一道防线。

3)如何在运行时更改映射(添加了新列)?

那么,文档中说明了什么?7.5。动态组件

这种映射的优点是仅通过编辑映射文档即可在部署时确定组件的实际属性的能力。(也可以使用DOM解析器对映射文档进行运行时操作。)

…使用DOM解析器。老实说,我不知道这意味着什么,如何实现。

以及代码映射中的 Adam Bar-
声明了动态组件(请参阅注释)

我认为动态组件在对象和数据库级别都不能真正动态。请注意,组成部分存储为普通列,因此我们需要知道它的列表…

但是Firo-NHibernate动态映射 有一个不错的主意
(看一下,对于某些提取来说太复杂了) 。如果确实需要,这可以解决真实的动态世界…运行时中的新列…中的新键映射IDictionary

摘要

通过<map>映射,我们可以变得非常动态。运行时中的不同键和值。无需重新部署。但是我们 不能 按选择或排序方式使用这些动态属性。这些值很难过滤

对于a,<dynamic- component>我们(开箱即用)依赖于映射。但是我们确实将数据存储在列中,因此可以使用联接来检索它们。易于过滤,排序。和/但必须有一些东西metadata可以指导我们我们所拥有的和我们能做的。

其他来源:

2020-05-19