NHibernate缩小代理警告

Dim*_*hev 7 nhibernate

我们正在构建一个使用NH进行数据访问的ASP.NET MVC应用程序.使用NH Profiler我看到很多警告,例如"WARN:将代理缩小到Domain.CaseTask - 此操作中断==".在对每个子类的表中映射的类执行查询时,我经常得到这些,例如,使用NH Linq提供程序:

Query<ICaseTask>().Where(c => c.Assignee == Of || c.Operator == Of)
Run Code Online (Sandbox Code Playgroud)

CaseTask类从Task继承的地方,触发警告.

关于互联网上警告的信息很少,并且大多暗示这是可以忽略的......这个警告到底有什么警告?这应该是我应该寻求纠正的吗?

csh*_*olk 6

现实更加复杂。当您使用或者加载实体session.Load或访问延迟加载的属性时,NHibernate将返回一个代理对象。首次访问代理对象的任何属性时,该代理对象将被水化(数据将从DB中加载)。为了实现这一点,NHibernate生成了代理类,该代理类扩展了实体类并覆盖了所有属性的获取器和设置器。当不使用继承时,此方法非常有效,因为您将无法区分代理类和实体类(代理基类),例如简单的测试proxy is MyEntity将始终有效。

现在假设我们有一个Person实体:

class Person {
  // lazy-loaded
  public Animal Pet { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我们还有Animal类层次结构:

public abstract class Animal { ... }
public class Cat { ... }
public class Dog { ... }
Run Code Online (Sandbox Code Playgroud)

现在假设该Pet属性是延迟加载的,当您向NHibernate请求person宠物时,您将获得一个代理对象:

var pet = somePerson.Pet; // pet will be a proxy
Run Code Online (Sandbox Code Playgroud)

但是由于PetNH是延迟加载的属性,它不会知道它是a Cat还是a的实例Dog,因此它会尽力而为,并且创建一个扩展的代理Animal。代理会通过测试pet is Animal,但会失败试验两种pet is Catpet is Dog

现在,假设您将访问pet对象的某些属性,强制NH从DB加载数据。现在,NH将知道您的宠物是例如,Cat但是代理已经生成并且无法更改。这将迫使NHibernate发出警告,pet指出该扩展类型的原始代理Animal将缩小为type Cat。这意味着从现在开始pet.Id,您使用的动物代理对象将从现在开始session.Load<Animal>(pet.Id)扩展Cat。这也意味着,由于Cat现在存储为会话的一部分,因此,如果我们加载与第一个共享cat的第二个人,则NH将使用已经可用的Cat代理实例来填充延迟加载的属性。

结果之一就是对象引用pet将不同于session.Load<Animal>(pet.Id)(从object.ReferencesEqual某种意义上)获得的引用。

// example - say parent and child share *the same* pet
var pet = child.Pet; // NH will return proxy that extends Animal
pet.DoStuff(); // NH loads data from DB

var parent = child.Parent; // lazy-loaded property
var pet2 = parent.Pet; // NH will return proxy that extends Cat

Assert.NotSame(pet, pet2);
Run Code Online (Sandbox Code Playgroud)

现在,当这可能对您造成伤害时:

  1. 当您在代码中将实体放入Sets或Dictionaryies时,或者使用任何其他需要结Equals/GetHashCode对工作的结构时。通过提供自定义Equals/GetHashCode实现,可以轻松解决此问题(请参阅:http : //www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html? page = 1

  2. 当您尝试将代理对象强制转换为目标类型时,例如(Cat)pet,但是同样存在已知的解决方案(例如,在NHibernate中获取正确类型的代理

因此,道理是在域模型中尽可能避免继承。


Fou*_*rth 1

此警告是关于具有子类属性或字段的类。IE:

public class Animal
{
    public int Id {get;set;}
}

public class Cat : Animal
{
    public int Weight {get;set;}
}

public class Person
{
    public Cat Pet {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

NHibernate 在加载 person 实体时会感到不安,因为它不想为您进行转换,因为行为变得不可预测。除非您告诉 NHibernate 如何处理 Equals(以及其他逻辑),否则它不会知道如何自行进行比较。

纠正这个问题的基本想法是让 NHibernate 将基类对象放入图中,然后处理转换(请注意,此设置将使用一些略有不同的映射 - 我这样做是为了简化代码,但显然可以完成通过将属性保留为完整的 getter/setter):

public class Animal
    {
        public int Id {get;set;}
    }

public class Cat : Animal
{
    public int Weight {get;set;}
}

public class Person
{
    private Animal _pet;
    public Cat Pet {
        get{return _pet as Cat;}
    }
}
Run Code Online (Sandbox Code Playgroud)