2012-07-02 208 views
4

我試圖弄清楚這個問題相當長的時間。我有一個黑客的方式來使它工作。Fluent Nhibernate複合鍵映射

我只想知道這是否可能在流利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其收集,如果我通過call 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

還是我不得不說

ClientCommission.ComissionLevel1 = newCommissionLevel; 
ClientCommission.ComissionLevel2 = newCommissionLevel; 
  1. 對於我得到的異常,這是因爲NHibernate不會生成正確的列,它似乎會生成th ree年份專欄。因爲如果我手動創建兩個名爲ComissionLevelID1和CommissionLevelID2的屬性,請禁用ClientCommission上的.Insert()和.Update()它將正確保存它。

有人能告訴我映射這兩個類的正確方法嗎?

非常感謝。

+0

只是一個想法。如果你正在介紹所有這些複雜性。你可能做錯了什麼。簡化您的映射和設計。你以後不會再維護它。 – DarthVader

回答

5

簡短的回答:你不能對多重引用

份額列

長回答: NHibernate的對待每一個參考相互獨立的,但的確消除了在插入語句重複列,因此引用嘗試訪問不再存在的列。它是這樣做的,因爲如果共享列在對象模型中的引用之間不同,它不能確定哪一個是正確的。

如果您可以更改數據庫架構並使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 
+0

非常感謝。不幸的是我不能改變db_schema。 DBA不允許我們這樣做。我可以問你在哪裏閱讀了關於NHibernate的知識?有沒有文檔? – user1494907

+0

其中大部分是遺留數據庫的痛苦經驗。我通過Google搜索發現了原因,閱讀了NH來源,試驗和錯誤。每次遇到這樣的問題,你都必須妥協。要麼以不同的方式進行映射(例如,如果部分是唯一的,則忽略db中的主鍵/外鍵),將私有屬性流入域模型,實現nhibernate掛鉤以避開NHibernate的默認處理。 – Firo

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)