Реальность сложнее.Когда вы загружаете сущность с помощью session.Load
или обращаетесь к свойству с отложенной загрузкой, NHibernate возвращает прокси-объект.Этот прокси-объект будет гидратирован (данные будут загружены из БД) при первом обращении к любому из его свойств.Для этого NHibernate генерирует прокси-класс, который расширяет класс сущностей и переопределяет все методы получения и установки свойств.Это прекрасно работает, когда наследование не используется, поскольку у вас не будет возможности провести различие между прокси и классом сущности (базовый класс прокси), например, простой тест proxy is MyEntity
всегда будет работать.
Теперь представьте, что у нас есть Personentity:
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 загрузить данные из БД.Теперь NH будет знать, что ваш питомец, например, Cat
, но прокси уже создан и не может быть изменен.Это заставит NHibernate выдать предупреждение о том, что исходный прокси для pet
, который расширяет тип Animal
, будет сужен до типа Cat
.Это означает, что с этого момента прокси-объект для животных с pet.Id
, который вы создаете с помощью session.Load<Animal>(pet.Id)
, будет расширяться Cat
с этого момента.Это также означает, что, поскольку Cat
теперь хранится как часть сеанса, если мы загрузим второго человека, который делит cat с первым, NH будет использовать уже доступный экземпляр Cat
proxy для заполнения свойства lazy -агрегата.
Одним из последствий будет то, что ссылка на объект 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
s или 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
, но опять же есть известные решения (например, Получение прокси правильного типа в NHibernate )
Таким образом, мораль состоит в том, чтобы максимально избежать наследования в вашей доменной модели.