现实更加复杂。当您使用session.Load 加载实体或访问延迟加载的属性时,NHibernate 将返回一个代理对象。当您第一次访问它的任何属性时,该代理对象将被水合(数据将从数据库加载)。为了实现这一点,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 询问 person pet 时,你会得到一个代理对象:
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 发出警告,即扩展类型 Animal 的 pet 的原始代理将缩小为类型 Cat。这意味着从现在开始您使用session.Load<Animal>(pet.Id) 创建的具有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);
现在这可能会对您造成伤害:
当您将实体放入代码中的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)
当您尝试将代理对象转换为目标类型时,例如(Cat)pet,但同样有已知的解决方案(例如Getting proxies of the correct type in NHibernate)
因此,道德是尽可能避免在您的域模型中继承。