NHibernate缩小代理警告

时间:2011-11-25 10:44:47

标签: nhibernate

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

Query<ICaseTask>().Where(c => c.Assignee == Of || c.Operator == Of)

CaseTask类继承自Task,触发​​警告。

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

2 个答案:

答案 0 :(得分:5)

现实更加复杂。当您使用session.Load加载实体或访问延迟加载的属性时,NHibernate会返回一个代理对象。当您第一次访问其任何属性时,该代理对象将通过水合(数据将从DB加载)。为了实现这一点,NHibernate生成扩展实体类的代理类,并覆盖所有属性getter和setter。这在未使用继承时非常有效,因为您无法区分代理和实体类(代理基类),例如,简单测试proxy is MyEntity将始终有效。

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

class Person {
  // lazy-loaded
  public Animal Pet { get; set; }
}

我们还有Animal类层次结构:

public abstract class Animal { ... }
public class Cat { ... }
public class Dog { ... }

现在假设Pet属性是延迟加载的,当你问NHibernate的人宠物时,你会得到一个代理对象:

var pet = somePerson.Pet; // pet will be a proxy

但是由于Pet是延迟加载的属性,NH不会知道它是Cat还是Dog的实例,所以它会尽力而为,并创建一个代理延伸Animal。代理将通过pet is Animal的测试,但无法对pet is Catpet is Dog进行测试。

现在假设您将访问pet对象的某些属性,强制NH从DB加载数据。现在NH会知道你的宠物是例如Cat,但代理已生成且无法更改。 这将强制NHibernate发出警告,指出pet扩展类型为Animal的原始代理将缩小为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);

现在这可能会对您造成伤害:

  1. 当您将实体放入代码中的SetDictionary或者使用需要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,但又有解决方案(例如Getting proxies of the correct type in NHibernate

  3. 因此,道德是尽可能避免域模型中的继承。

答案 1 :(得分:2)

此警告是关于具有作为子类的属性或字段的类。 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;}
}

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;}
    }
}