Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在NHibernate中获取正确类型的代理_C#_.net_Nhibernate_Proxy - Fatal编程技术网

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

C# 在NHibernate中获取正确类型的代理,c#,.net,nhibernate,proxy,C#,.net,Nhibernate,Proxy,我对nhibernate中未初始化的代理有问题 域模型 假设我有两个平行的类层次结构:Animal、Dog、Cat和AnimalOwner、DogOwner、CatOwner,其中Dog和Cat都继承自AnimalOwner,DogOwner和CatOwner都继承自AnimalOwner。AnimalOwner有一个名为OwnedAnimal的动物类型参考 下面是示例中的类: public abstract class Animal { // some properties } pub

我对nhibernate中未初始化的代理有问题

域模型

假设我有两个平行的类层次结构:Animal、Dog、Cat和AnimalOwner、DogOwner、CatOwner,其中Dog和Cat都继承自AnimalOwner,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中设置一只狗,在CatOwner中设置一只猫

问题

我有这样的代码:

public void ProcessDogOwner(DogOwner owner)
{
   Dog dog = (Dog)owner.OwnedAnimal;
   ....
}
Dog dog = dogOwner.OwnedAnimal.CastEntity<Dog>();
这个方法可以被许多不同的方法调用,在大多数情况下,狗已经在内存中,一切正常,但很少狗还没有在内存中-在这种情况下,我得到了一个nhibernate“未初始化代理”,但是cast抛出了一个异常,因为nhibernate生成了动物而不是狗的代理

我知道nhibernate就是这样工作的,但我需要知道类型而不加载对象——或者更准确地说,我需要未初始化的代理是猫或狗的代理,而不是动物的代理

约束

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

Nir

您可能希望尝试此操作以查看代理类型(假设NH 2.0+):


但是这种类型的施法或“类型窥视”是非常糟糕的实践…

对于动物类来说,关闭延迟加载是最容易的。你说它大部分都在记忆中

<class name="Animal" lazy="false">
<!-- ... -->
</class>
据我所知,只有当
AnimalOwner
实际上是一个代理时,它才起作用

您可以在animal owner上使用泛型,使引用成为一个具体的类

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

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

你和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;
        }
    }
publicstatict castenty(这个对象实体),其中T:class
{
var proxy=作为INHibernateProxy的实体;
如果(代理!=null)
{
将proxy.HibernateLazyInitializer.GetImplementation()返回为T;
}
其他的
{
将实体返回为T;
}
}
可以这样使用:

public void ProcessDogOwner(DogOwner owner)
{
   Dog dog = (Dog)owner.OwnedAnimal;
   ....
}
Dog dog = dogOwner.OwnedAnimal.CastEntity<Dog>();
Dog Dog=dogOwner.OwnedAnimal.CastEntity();

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


然后可以将其铸造为正确的“动物”。发生的情况是,原始对象有一个对nhibernate代理对象的引用(当它被延迟加载时),该对象充当通过Animal类公开的所有方法的Animal(它将所有调用传递给加载的对象)。然而,它不能被铸造为任何其他动物,因为它不是这些,它只是模仿动物类。然而,AnimalProxy封装的类可以被铸造为子类animal,因为它是正确类的真实实例,您只需要访问它的
这个
引用。

如果我们一直在处理相同的问题,问题是生成的代理是动物的代理,而不是狗的代理

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

Dog dog = this.CurrentSession.Load<Dog>(owner.OwnedAnimal.AnimalID);
Dog Dog=this.CurrentSession.Load(owner.OwnedAnimal.AnimalID);
这将返回到会话并重新加载具有正确类型的对象


希望这对您有所帮助

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

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
公共类DogOwnerMapOverride:IAAutoMappingOverride
{
公共无效替代(自动映射)
{
mapping.References(x=>x.OwnedAnimal).Not.LazyLoad();
}
}

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

public virtual T As<T>() where T : Entity {
      return this as T;
}
public virtual T As(),其中T:Entity{
将此作为T返回;
}

谢谢,我不确定我能不能在我的案例中使用这些技术,但我会去看看。添加到其他选项(
无代理和
Castenty
)中。Castenty是一个很好的尝试,但我遇到了一个问题:如果你有父类和子类,并且Parent类型的实例“p”实际上是一个子类(多态性),强制转换在编译时失败,即使它在运行时可以工作。还要注意,您的第一个“INhibernateProxy”在“h”上缺少大写字母。换成“原样”演员没什么帮助,但“自我”技巧奏效了。我不理解你的亲子问题。如果父对象是子对象,则子对象需要从父对象派生,并且编译器不应出现任何问题。不幸的是,lazy=没有代理仍然被破坏,请参见一个示例。这实际上不会再次加载对象,除非您同时创建了一个新会话,这是不推荐的。NH确保在会话的整个生命周期中,数据库中的同一个实例在内存中都有相同的实例。实际上,我们回到了上面的“自我”解决方案,因为我们发现它更可靠,尽管这是我们书中给出的解决方法。
public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
public virtual T As<T>() where T : Entity {
      return this as T;
}