在NHibernate中获取正确类型的代理

时间:2009-01-05 14:22:44

标签: c# .net nhibernate proxy

我在nhibernate

中遇到了未初始化的代理问题

域名模型

假设我有两个并行的类层次结构:Animal,Dog,Cat和AnimalOwner,DogOwner,CatOwner,其中Dog和Cat都继承自Animal和DogOwner,CatOwner都继承自AnimalOwner。 AnimalOwner有一个名为OwnedAnimal的类型为Animal的引用。

以下是示例中的类:

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代表Animal而不是Dog。

我知道这是nhibernate的工作方式,但我需要知道类型而不加载对象 - 或者更正确的是我需要未初始化的代理来代替Cat或Dog而不是Animal的代理。

约束

  • 我无法更改域模型,模型由另一个部门交给我,我试图让他们更改模型并失败。
  • 实际模型比示例复杂得多,并且类之间有很多引用,出于性能原因,使用预先加载或向查询添加联接是不可能的。
  • 我完全控制了源代码,hbm映射和数据库模式,我可以按照我想要的方式更改它们(只要我不改变模型类之间的关系)。
  • 我有很多方法,例如示例中的方法,我不想修改所有方法。

谢谢,
尼尔

6 个答案:

答案 0 :(得分:24)

关闭动物类的延迟加载是最容易的。无论如何,你说这主要是在记忆中。

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

作为其变体,您也可以使用no-proxy,请参阅this post

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

据我所知,它仅在AnimalOwner实际上是代理时才有效。

您可以在动物所有者上使用泛型来使引用成为具体类。

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

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

您可以在单独的表格中映射DogOwnersCatOwners,并在映射中定义具体的动物类型。

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

你在this blog提出的NHibernate中乱七八糟。 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>();

答案 1 :(得分:12)

我认为我们最近遇到了类似的问题,AFAIR解决方案是给'动物'一个自我 - “方法/属性”:

public Animal Self { get { return this; } }

然后可以将其转换为纠正“动物”。发生的事情是你的原始对象引用了nhibernate代理对象(当它被延迟加载时),它对通过Animal类公开的所有方法充当Animal(它将所有调用传递给加载的对象)。然而,它不能像你的任何其他动物一样被铸造,因为它不是这些,它只模仿动物类。但是,由AnimalProxy封装的类可以作为子类化动物生成,因为它是正确类的真实实例,您只需要获得它的this引用。

答案 2 :(得分:1)

您可以尝试将此方法放在基础实体上:

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

答案 3 :(得分:0)

您可能想尝试这样来查看代理类型(假设NH 2.0 +):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

但是这种演员或“偷看”无论如何都是非常糟糕的做法......

答案 4 :(得分:0)

如果我们一直在处理同样的问题,那么问题就是生成的代理是Animal的代理而不是Dog。

我们使用的解决方案是重新加载对象:

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

这会返回到您的会话并使用正确的类型重新加载对象。

希望这有帮助

答案 5 :(得分:0)

如果您使用Fluent NHibernate,则可以使用自动映射覆盖来仅关闭该属性的延迟加载:

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