5

在我的 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; }
}

我可以映射这个类吗?

4

1 回答 1

11

这个话题很有趣。乍一看可能不太清楚。把我的回答作为一个概述,一个来自下面列出的所有来源的总结。也许它会给你答案。

从 C# 的角度来看,您可以这样考虑这些动态属性

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

public virtual IDictionary Additional { get; set; }

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

要将数据加载到这些字典之一中,我们必须(在大多数情况下)进行不同的映射并具有不同的表结构。Generic 可以方便地转换rows中包含的数据。Non-Generic 可用于映射(如问题中的示例)。让我们讨论两者

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 的示例。三元协会。涉及三个实体,我们在运行时仍然具有灵活性。如前所述,我们可以插入新的 PersonTypes 并修改这些 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) 所示,以某种方式管理要用作key的值集总是更好的主意。这就是我们讨论三元关联的原因。因为我们迟早必须解释这本字典中的数据——使用一些元数据,将它们也用作键可能会很方便......

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>

在这种情况下,我们确实有分隔表ElementValues,连接到我们的父实体(作为其<class>映射的一部分)。

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

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

这些可能需要一些更特殊的处理(插入、更新、删除)

加入会简化很多东西,但总是会在 SQL 语句中使用(即使只需要核心属性)

是动态的吗?

好吧,我们获得了:

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

我们失去了:

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

2b) 字典和元数据

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

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

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

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

...使用 DOM 解析器。老实说,我不知道那是什么意思,如何实施。

以及Mapping-by-Code中的 Adam Bar - 动态组件说明(见评论)

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

但是Firo - NHibernate 动态映射有一个好主意 (看看,对于某些提取来说太复杂了)。如果真的需要,这可能是现实动态世界的解决方案......运行时的新列......新的键映射IDictionary

概括

通过<map>映射,我们可以非常动态。运行时不同的键和值。无需重新部署。但是我们不能在 select 或 order by 中使用这些动态属性。很难按这些值过滤

有了 a<dynamic-component>我们(开箱即用)依赖于映射。但是我们确实在列中有数据,因此我们可以使用连接来检索它们。易于过滤、排序。和/但必须有一些metadata来指导我们我们拥有什么以及我们可以做什么。

其他来源:

于 2013-05-22T13:03:32.190 回答