流畅的Nhibernate复合键映射

时间:2012-07-02 01:14:37

标签: nhibernate fluent-nhibernate fluent-nhibernate-mapping

我试图在很长一段时间内弄清楚这个问题。我有一种愚蠢的方法让它发挥作用。

我只是想知道这是否可以在Fluent NHibernate映射中使用。

假设我有两个表格,例如:

Table ComissionLevel
{
    Year,
    ComissionLevelID,

    ... other properties ....
}
primary key (Year,ComissionLevelID)

Table ClientCommission
{
    Year,
    ClientID,
    CommissionLevelID_1,
    CommissionLevelID_2,

    ... other properties ...
}
primary key (Year,ClientID)
foreign key CommissionLevel1 (Year,CommissionLevelID_1)
foreign key CommissionLevel2 (Year,CommissionLevelID_2)

目前我的映射如下:

public ComissionLevelMap()
{
  Schema("XXXX");
  Table("ComissionLevel");
  LazyLoad();
  CompositeId()
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } )
    .KeyProperty(x => x.CommissionLevelID, set => {
        set.ColumnName("CommissionLevelID");
        set.Length(10);
        set.Access.Property(); } );

  HasMany<ClientCommission>(x => x.ClientCommissions)
    .Access.Property()
    .AsSet()
    .Cascade.AllDeleteOrphan()
    .LazyLoad()
    .Inverse()
    .Generic()
    .KeyColumns.Add("Year", mapping => mapping.Name("Year")
                                                         .SqlType("NUMBER")
                                                         .Nullable())
    .KeyColumns.Add("CommissionLevelID_1", mapping => mapping.Name("CommissionLevelID_1")
                                                         .SqlType("VARCHAR2")
                                                         .Nullable()
                                                         .Length(10));
  HasMany<ClientCommission>(x => x.ClientCommission2s)
    .Access.Property()
    .AsSet()
    .Cascade.AllDeleteOrphan()
    .LazyLoad()
    .Inverse()
    .Generic()
    .KeyColumns.Add("Year", mapping => mapping.Name("Year")
                                                         .SqlType("NUMBER")
                                                         .Nullable())
    .KeyColumns.Add("CommissionLevelID_2", mapping => mapping.Name("CommissionLevelID_2")
                                                         .SqlType("VARCHAR2")
                                                         .Nullable()
                                                         .Length(10));
}

public ClientCommissionMap()
{
  Schema("XXXXX");
  Table("ClientCommission");
  LazyLoad();
  CompositeId()
    .KeyProperty(x => x.ClientID, set => {
        set.ColumnName("ClientID");
        set.Length(10);
        set.Access.Property(); } )
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } );
  References(x => x.ComissionLevel1)
    .Class<ComissionLevel>()
    .Access.Property()
    .Cascade.None()
    .LazyLoad()
    .Insert()
    .Update()
    .Columns("Year", "CommissionLevelID_1");
  References(x => x.ComissionLevel2)
    .Class<ComissionLevel>()
    .Access.Property()
    .Cascade.None()
    .LazyLoad()
    .Insert()
    .Update()
    .Columns("Year", "CommissionLevelID_2");

}

我现在的问题是每当我创建一个CommissionLevel并将ClientCommission分配给它的集合时,如果我通过调用session.save(CommissionLevel)保存它们,它会抛出异常

<Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index>.

我的问题是:

  1. NHibernate会自动保存关系吗?像:

        ClientCommission commission = new ClientCommission{Year = 2012, ClientID =SomeGuid};
        CommissionLevel  newCommissionLevel = new CommissionLevel{Year = 2012, CommissionLevelID =NewCommissionLevelGuid};
    
        newCommissionLevel.ClientCommission1s.Add(commission);
        newCommissionLevel.ClientCommission2s.Add(commission);
    
        CommissionLevelRepo.Save(newCommissionLevel);
    

    当我调用CommissionLevelRepo.Save(newCommissionLevel)时,NHibernate是否也会更新ClientCommission.ComissionLevel1和ClientCommission.ComissionLevel2

  2. 或者我必须说

    ClientCommission.ComissionLevel1 = newCommissionLevel; 
    ClientCommission.ComissionLevel2 = newCommissionLevel; 
    
    1. 对于我得到的异常,这是因为NHibernate没有生成正确的列,它似乎会生成三个Year列。如果我手动创建两个名为ComissionLevelID1和CommissionLevelID2的属性,请在ClientCommission上禁用.Insert()和.Update(),它将正确保存它。
    2. 有人能告诉我一个正确的方法来映射这两个类吗?

      非常感谢。

2 个答案:

答案 0 :(得分:6)

简答:您无法共享多个参考的列

长答案: NHibernate会将每个引用视为彼此独立,但确实消除了insert语句中的重复列,因此引用会尝试访问不再存在的列。它是这样做的,因为如果共享列在对象模型中的to引用之间不同,则无法确定哪一个是正确的。

如果您可以更改数据库架构并使ID唯一,则在ID和引用中一起忽略年份。

更新

你可以简化一些映射

CompositeId()
    .KeyProperty(x => x.Year, set => {
        set.ColumnName("Year");
        set.Access.Property(); } )
    .KeyProperty(x => x.CommissionLevelID, set => {
        set.ColumnName("CommissionLevelID");
        set.Length(10);
        set.Access.Property(); } );

// to
CompositeId()
    .KeyProperty(x => x.Year)  // columnname is equal propertyname by default
    .KeyProperty(x => x.CommissionLevelID, set => set.Length(10).Access.Property());  // property is default access and can also be left out


.SqlType("VARCHAR2").Length(10)
// to
.Length(10) or .SqlType("VARCHAR2")
// because length is ignored when sqltype is specified

答案 1 :(得分:0)

对于复合键,请查看Mapping Composite keys in Fluent NHibernate

为了简化映射,您可以将主键更改为仅一个键并创建唯一索引来表示它,但不是最佳解决方案。

之前(复合键):

CREATE TABLE XPTO ( COD_XPTO1 INT NOT NULL IDENTITY,
                    COD_XPTO2 INT NOT NULL,
                    TXT_XPTO VARCHAR(10) NOT NULL)
ALTER TABLE XPTO
  ADD CONSTRAINT PK_XPTO (COD_XPTO1, COD_XPTO2)

之后(具有唯一索引的单键):

CREATE TABLE XPTO ( COD_XPTO1 INT NOT NULL IDENTITY,
                    COD_XPTO2 INT NOT NULL,
                    TXT_XPTO VARCHAR(10) NOT NULL)

ALTER TABLE XPTO
  ADD CONSTRAINT PK_XPTO (COD_XPTO1)

CREATE UNIQUE INDEX UK_XPTO ON XPTO (COD_XPTO1, COD_XPTO2)