我们正在构建一个使用NH进行数据访问的ASP.NET MVC应用程序。使用NH Profiler我看到很多警告,例如“WARN:将代理缩小到Domain.CaseTask - 此操作中断==”。在为每个子类的表映射的类执行查询时,我经常得到这些,例如,使用NH Linq提供程序:
Query<ICaseTask>().Where(c => c.Assignee == Of || c.Operator == Of)
CaseTask类继承自Task,触发警告。
有关互联网上警告的信息很少,并且大多暗示这是可以忽略的......这个警告对此有何警告?这应该是我应该寻求纠正的吗?
答案 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 Cat
或pet 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);
现在这可能会对您造成伤害:
当您将实体放入代码中的Set
或Dictionary
或者使用需要Equals/GetHashCode
对的任何其他结构时才能使用。通过提供自定义Equals/GetHashCode
实施可以轻松解决此问题(请参阅:http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=1)
当您尝试将代理对象转换为目标类型时,例如(Cat)pet
,但又有解决方案(例如Getting proxies of the correct type in NHibernate)
因此,道德是尽可能避免域模型中的继承。
答案 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;}
}
}