【问题标题】:NHibernate mapping table of tablesNHibernate 表的映射表
【发布时间】:2026-01-30 16:30:01
【问题描述】:

我正在尝试将 NHibernate 包装在遗留数据库(不是我创建的)周围。它具有我最能形容为“表的表”的内容,称为 TABLE_DETAIL。它看起来像这样:

COLUMN_NAME  |  TABLE_VALUE    |  TABLE_DESC
-------------+-----------------+---------------------------------
state        |  CA             |  California
state        |  NY             |  New York
...
country      |  US             |  United States
country      |  CA             |  Canada

我正在尝试使用 Fluent NHibernate 和 table-per-class-hierarchy 策略来映射它。换句话说,我有一个 TableDetail 类,以及 State 和 Country 的子类。我使用 COLUMN_NAME 作为鉴别器。

TableDetailMap.cs:

public class TableDetailMap : ClassMap<TableDetail>
{
  public TableDetailMap()
  {
    Table("TABLE_DETAIL");
    CompositeId()
      .KeyProperty(x => x.TableValue, "TABLE_VALUE")
      .KeyProperty(x => x.ColumnName, "COLUMN_NAME");
    Map(x => x.ColumnName).Column("COLUMN_NAME");
    Map(x => x.TableDesc).Column("TABLE_DESC");
    DiscriminateSubClassesOnColumn("COLUMN_NAME");
  }
}

StateMap.cs:

public class StateMap : SubclassMap<State>
{
  public StateMap()
  {
    DiscriminatorValue("state");
  }
}

TABLE_DETAIL 因此有一个复合键(由 COLUMN_NAME/TABLE_VALUE 组成),而鉴别器就是这些字段之一。我的问题是 NHibernate 期望复合键的两个组件都在另一个表中被引用 - 但它们不应该被引用,因为一个是由鉴别器定义的。

例如,我有 ADDRESS_RECORD 表:

LINE_1           |  CITY           |  STATE    |  ZIP
-----------------+-----------------+-----------+----------------
123 Any Street   |  Anytown        |  CA       |  12345

当我尝试将我的“状态”字段映射到我的状态类时,问题就出现了。 ADDRESS_RECORD 中的 STATE 列指的是 TABLE_DETAIL 主键的一半 - TABLE_VALUE 部分。 COLUMN_NAME 部分是“状态” - 因为它是鉴别器,我希望应该提供它。但是NHibernate不这么认为,抛出了这个异常:

Foreign key (FK3D33E87CA66E339C:ADDRESS_RECORD [STATE])) must have same number of columns as the referenced primary key (TABLE_DETAIL [TABLE_VALUE, COLUMN_NAME])

我如何映射它以便 NHibernate 知道自动提供“状态”作为复合键后半部分的值?

如果我能提供更多信息,请告诉我。

【问题讨论】:

    标签: .net fluent-nhibernate


    【解决方案1】:

    我想出了一个马马虎虎的解决方法。在基类映射 (TableDetailMap.cs) 中,我指定了鉴别器,我指定了“始终使用值选择”,如下所示:

    public class TableDetailMap : ClassMap<TableDetail>
    {
      public TableDetailMap()
      {
        ...snip...
    
        DiscriminateSubClassesOnColumn("COLUMN_NAME").AlwaysSelectWithValue();
      }
    }
    

    然后,在所有使用 TableDetail 对象的映射类中,我必须指定在获取时使用“select”,如下所示:

    public class AddressRecordMap : ClassMap<AddressRecord>
    {
      public AddressRecordMap()
      {
        ...snip...
    
        References(x => x.State)
          .Column("STATE")
          .Fetch.Select();
    
        ...snip...
      }
    }
    

    在我将这些对象之一用作复合 ID 的一部分的情况下,这仍然不起作用。我不确定如何解决这个问题,但我现在能够解决它。

    【讨论】: