2012-09-10 50 views
0

我有這樣的NHibernate的模型:NHibernate的 - 獲取引用的列值

public class RootTable 
{ 
    public virtual string Name { get; set; } 
    public virtual string Description { get; set; } 
    public virtual DateTime? Start { get; set; } 
    public virtual DateTime? Finish { get; set; } 
    public virtual IList<Leaf1> ChildCollection1 { get; set; } 
} 

public class Leaf1 
{ 
    public virtual int ID { get; set; } 
    public virtual string Info1 { get; set; } 
    public virtual string Info2 { get; set; } 
    public virtual RootTable Parent { get; set; } 
} 

public class RootMapping : ClassMap<RootTable> 
{ 
    public RootMapping() 
    { 
     Table("RootTable"); 
     Id(c => c.Name); 
     Map(c => c.Description, "Desc").Length(20); 
     Map(c => c.Start).Length(20); 
     Map(c => c.Finish).Length(20); 
     HasMany(c => c.ChildCollection1) 
      .Cascade.All() 
      .LazyLoad() 
      .Inverse(); 
    } 
} 

public class Leaf1Mapping : ClassMap<Leaf1> 
{ 
    public Leaf1Mapping() 
    { 
     Table("LeafTable1"); 
     Id(c => c.ID, "RowID").GeneratedBy.Identity(); 
     Map(c => c.Info1).Length(20); 
     Map(c => c.Info2).Length(20); 
     References(c => c.Parent).Column("Parent").LazyLoad(); 
    } 
} 

我試圖做的是無惰性加載RootTable訪問Leaf1引用的列的

。換句話說,我有這樣的:

this.LogMessage("Loading leaves..."); 
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>(); 
this.LogMessage("Loaded leaves..."); 
var leaf = allleafs[0]; 

this.LogMessage("Leaf metadata:"); 
// This causes a lazy-load of the RootTable object. 
this.LogMessage("Leaf parent is " + leaf.Parent); 

現在我真正想要的是值「父」,因爲它是存儲在底層數據庫的 - 我不在乎父對象,我不想加載它,我只想要獲得原始值。我不能訪問包含該值的字段(即leaf.Parent.Name),我想這在一個通用的方式工作...

[背景] 終究是堵到使用的審計框架NHibernate攔截器,所以這需要以通用的方式工作,以便任何傳入的對象都可以報告更改後的值。這是完全可能的,子節點將不會改變根節點,所以當調用攔截器的OnFlushDirty()時,我做而不是希望攔截器導致其他對象的延遲加載。

我知道我可以直接引用父屬性(例如,我可以說「leaf.Parent.Name」),這將得到我的價值沒有惰性負載,但似乎並沒有一個快速的方法以確定「名稱」是我想要返回的關鍵屬性。

[編輯補充...]
走在樹似乎並沒有因爲我得到一個空引用異常工作:

var theType = leaf.Parent.GetType(); 

// This line returns a NULL due to the proxy class. 
var metadata = factory.GetClassMetadata(theType); 
var idProp = metadata.IdentifierPropertyName; 

var prop = theType.GetProperty(idProp); 
var val = prop.GetValue(leaf.Parent, null); 

this.LogMessage("Leaf parent is " + val); 

現在,theType回來爲RootTableProxy,所以是隻是一個佔位符,因爲主類沒有加載。這意味着metadata爲空,因爲沒有類別元數據,因此idProp失敗並出現空引用異常。

所以我實際上看不到如何在沒有延遲加載的情況下獲得引用的列值:肯定這是不對的?

編輯追加(更多!)
我想到了一個簡單的解決方案已被發現使用session.GetIdentifier()。然而,這似乎並不適用於所有情況:在攔截器中調用session.GetIdentifier(state[i])對某些對象造成異常,指出該對象不屬於當前會話的一部分,因此仍在尋找一種更可靠的解決方案,而不會訴諸於反射。任何想法歡迎...

回答

1

如何將另一個屬性添加到您的Leaf1類。

例如

public class Leaf1 
{ 
    public virtual int ID { get; set; } 
    public virtual string Info1 { get; set; } 
    public virtual string Info2 { get; set; } 
    public virtual RootTable Parent { get; set; } 
    public virtual int ParentId { get; set; } 
} 

,然後地圖是隻讀

public class Leaf1Mapping : ClassMap<Leaf1> 
{ 
    public Leaf1Mapping() 
    { 
     Table("LeafTable1"); 
     Id(c => c.ID, "RowID").GeneratedBy.Identity(); 
     Map(c => c.Info1).Length(20); 
     Map(c => c.Info2).Length(20); 
     References(c => c.Parent).Column("Parent").LazyLoad(); 
     Map(c => c.ParentId).Column("Parent").ReadOnly(); 
    } 
} 
+0

這是一個想法 - 我們有一個大的領域模型,所以需要通過代碼添加這個屬性很大的步行。我們也許可以通過僅僅在那些需要它的屬性上進行限制 - 將仔細研究。雖然對於我認爲NH應該免費贈送給我的東西,似乎還是有些冒失......好吧。 –

0

你可以讓你的實體實現的接口(將清潔劑),但如果你想獲得的實際類型的代理,使用NHibernateUtil。GetClass(代理)將返回您正在尋找的基礎類型

0

它發現ISession有一個方法GetIdentifier它返回一個屬性的緩存值。以從我原來的職位的例子中,代碼更改爲:

this.LogMessage("Loading leaves..."); 
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>(); 
this.LogMessage("Loaded leaves..."); 
var leaf = allleafs[0]; 

this.LogMessage("Leaf metadata:"); 
this.LogMessage("Leaf parent is " + session.GetIdentifier(leaf.Parent)); 

現在在測試中,這似乎是很好的工作,並且可以從OnFlushDirty兩個currentState和previousState對象攔截器中調用。

我一直在試圖找到關於GetIdentifier的更多信息,但是NH文檔令人遺憾地缺乏,似乎很少有博客文章提到這一點。如果有人知道我需要知道與此方法有關的任何警告,問題或「流氓」,我會非常樂意聽到他們...

[編輯添加] ...事實證明,如果對象不是代理對象(!),則這不起作用。仍然在尋找一種可靠的方法來做到這一點,它不涉及反射或裝飾具有屬性的對象來識別字段。