2009-01-05 38 views
19

正確類型的代理我有在NHibernate的獲取NHibernate的

未初始化的代理問題的域模型

比方說,我有兩個平行的類層次結構:動物,狗,貓和AnimalOwner, DogOwner,CatOwner,Dog和Cat都繼承了Animal,DogOwner和CatOwner,均繼承了AnimalOwner。 AnimalOwner有一個叫做OwnedAnimal的動物類型的參考。

下面是類的實例:

public abstract class Animal 
{ 
    // some properties 
} 

public class Dog : Animal 
{ 
    // some more properties 
} 

public class Cat : Animal 
{ 
    // some more properties 
} 

public class AnimalOwner 
{ 
    public virtual Animal OwnedAnimal {get;set;} 
    // more properties... 
} 

public class DogOwner : AnimalOwner 
{ 
    // even more properties 
} 

public class CatOwner : AnimalOwner 
{ 
    // even more properties 
} 

的類有適當的nhibernate映射,所有屬性是持久的,一切可延遲加載是延遲加載。

應用程序業務邏輯只允許您在DogOwner中設置Dog,並在CatOwner中設置Cat。

的問題

我有這樣的代碼:

public void ProcessDogOwner(DogOwner owner) 
{ 
    Dog dog = (Dog)owner.OwnedAnimal; 
    .... 
} 

這種方法可以由許多不同勢方法被調用,在大多數情況下,狗已經在內存中一切正常,但很少這條狗不在記憶中 - 在這種情況下,我得到了一個nhibernate「未初始化的代理」,但演員拋出了一個異常,因爲nhibernate生成了一個代理動物而不是狗。

我明白這是nhibernate的工作方式,但我需要知道該類型而不加載對象 - 或者更正確地說,我需要未初始化的代理是貓或狗的代理,而不是代理的動物。

約束

  • 我不能改變域模型,該模型是由另一個部門交給我,我試圖讓他們改變模型和失敗。
  • 實際模型比示例複雜得多,並且類之間有許多引用,使用預先加載或向查詢添加連接對於性能原因而言是不成問題的。
  • 我完全控制了源代碼,hbm映射和數據庫模式,我可以根據需要更改它們(只要我不更改模型類之間的關係)。
  • 我有很多像這個例子中的方法,我不想修改所有的方法。

感謝,
尼爾

回答

24

關閉動物類的懶加載是最容易的。無論如何,你說它主要在記憶中。

<class name="Animal" lazy="false"> 
<!-- ... --> 
</class> 

作爲其中的一個變種,你也可以使用no-proxy,看到this post

<property name="OwnedAnimal" lazy="no-proxy"/> 

據我所看到的,只有當AnimalOwner實際上是一個代理工作。

OR

您可以使用對動物所有者仿製藥,使基準的具體類。

class AnimalOwner<TAnimal> 
{ 
    virtual TAnimal OwnedAnimal {get;set;} 
} 

class CatOwner : AnimalOwner<Cat> 
{ 
} 

class DogOwner : AnimalOwner<Dog> 
{ 
} 

OR

可以映射在單獨的表的DogOwnersCatOwners,並限定在所述映射的具體動物類型。

<class name="CatOwner"> 
    <!-- ... --> 
    <property name="OwnedAninal" class="Cat"/> 
</class> 
<class name="DogOwner"> 
    <!-- ... --> 
    <property name="OwnedAninal" class="Dog"/> 
</class> 

OR

你惹的NHibernate一點左右,在this blog建議。 NH實際上能夠返回代理之後的真實對象。擬議這兒有位更簡單的實現:

public static T CastEntity<T>(this object entity) where T: class 
    { 
     var proxy = entity as INHibernateProxy; 
     if (proxy != null) 
     { 
      return proxy.HibernateLazyInitializer.GetImplementation() as T; 
     } 
     else 
     { 
      return entity as T; 
     } 
    } 

可以使用這樣的:

Dog dog = dogOwner.OwnedAnimal.CastEntit<Dog>(); 
+0

謝謝,我不知道我可以使用這些技術在我的情況,但我會檢查出來。 – Nir 2009-04-16 12:52:04

+0

添加到其他選項(`no-proxy`和`CastEntity`)。 – 2011-05-20 12:45:08

+0

CastEntity是一個很好的嘗試,但是我遇到了一個問題:如果您有Parent和Child類,並且Parent類型的實例「p」實際上是一個Child(多態性),則該轉換將失敗編譯時,即使它會工作運行時間。另請注意,您的第一個「InhibernateProxy」在「h」上缺少資本。切換到「as」投沒有幫助,但「自我」技巧奏效。 – 2013-01-17 00:09:17

0

你可能想試試這個看代理型(假設NH 2.0+):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass 

但這種鑄造或 「類型偷看」反正是非常不好的練習......

12

我認爲,我們最近有一個類似的問題,AFAIR的解決方案是讓「動物」自我 - 「方法/財產」:

public Animal Self { get { return this; } } 

然後這可以鑄造糾正「動物」。會發生什麼是你的原始對象有一個對nhibernate代理對象的引用(當它被延遲加載時),對於通過Animal類暴露的所有方法(它將所有調用傳遞給加載的對象),它充當Animal。然而,它不能像任何其他動物一樣鑄造,因爲它不屬於這些動物,它只模仿動物類。然而,由AnimalProxy封裝的類可以作爲子類動物進行鑄造,因爲它是一個正確類的實例,您只需要獲得它的this參考。

0

如果我們一直在處理同樣的問題,問題是生成的代理是動物的代理而不是狗的代理。

我們使用的解決辦法是重新加載對象:

Dog dog = this.CurrentSession.Load<Dog>(owner.OwnedAnimal.AnimalID); 

這又回到了您的會話,並重新加載正確類型的對象。

希望這有助於

0

如果你使用流利NHibernate的,你可以使用自動映射覆蓋關閉延遲加載僅該屬性:

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner> 
{ 
    public void Override(AutoMapping<DogOwner> mapping) 
    { 
     mapping.References(x => x.OwnedAnimal).Not.LazyLoad(); 
    } 
} 
-1

你可以試試把這個方法對你基地實體:

public virtual T As<T>() where T : Entity { 
     return this as T; 
}