2009-01-05 43 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; 
}