Получение прокси правильного типа в NHibernate - PullRequest
19 голосов
/ 05 января 2009

У меня проблема с неинициализированными прокси в nhibernate

Модель предметной области

Допустим, у меня есть две параллельные иерархии классов: Animal, Dog, Cat и AnimalOwner, DogOwner, CatOwner, где Dog и Cat наследуются от Animal, а DogOwner и CatOwner наследуются от AnimalOwner. AnimalOwner имеет ссылку типа Animal с именем OwnedAnimal.

Вот классы в примере:

public abstract class Animal
{
   // some properties
}

public class Dog : Animal
{
   // some more properties
}

public class Cat : Animal
{
   // some more properties
}

public class AnimalOwner 
{
   public virtual Animal OwnedAnimal {get;set;}
   // more properties...
}

public class DogOwner : AnimalOwner
{
   // even more properties
}

public class CatOwner : AnimalOwner
{
   // even more properties
}

Классы имеют правильное отображение nhibernate, все свойства являются постоянными, а все, что может быть загружено с отложенной загрузкой, загружается с отложенной загрузкой.

Бизнес-логика приложения позволяет устанавливать только Dog в DogOwner и Cat в CatOwner.

Проблема

У меня есть такой код:

public void ProcessDogOwner(DogOwner owner)
{
   Dog dog = (Dog)owner.OwnedAnimal;
   ....
}

Этот метод может вызываться многими различными методами, в большинстве случаев собака уже в памяти, и все в порядке, но редко собака не находится в памяти - в этом случае я получаю nhibernate "неинициализированный прокси", но приведение вызывает исключение, потому что nhibernate создает прокси для Animal, а не для Dog.

Я понимаю, что так работает nhibernate, но мне нужно знать тип без загрузки объекта - или, более правильно, мне нужно, чтобы неинициализированный прокси был прокси Cat или Dog, а не прокси Animal.

Ограничения

  • Я не могу изменить модель домена, модель передана мне другим отделом, я попытался заставить их изменить модель, но потерпел неудачу.
  • Фактическая модель намного сложнее, чем пример, и классы имеют много ссылок между ними, поэтому из-за производительности не может быть и речи о загрузке или добавлении объединений в запросы.
  • У меня есть полный контроль над исходным кодом, отображением hbm и схемой базы данных, и я могу изменить их так, как я захочу (если я не изменю отношения между классами модели).
  • У меня есть много методов, подобных приведенному в примере, и я не хочу изменять их все.

Спасибо
Nir

Ответы [ 6 ]

24 голосов
/ 16 апреля 2009

Легче всего отключить ленивую загрузку для класса животных. Вы говорите, это в основном все в памяти.

<class name="Animal" lazy="false">
<!-- ... -->
</class>

Как вариант, вы также можете использовать no-proxy, см. этот пост :

<property name="OwnedAnimal" lazy="no-proxy"/>

Насколько я вижу, он работает только тогда, когда AnimalOwner на самом деле является прокси.

OR

Вы можете использовать дженерики для владельца животного, чтобы сделать ссылку конкретным классом.

class AnimalOwner<TAnimal>
{
  virtual TAnimal OwnedAnimal {get;set;}
}

class CatOwner : AnimalOwner<Cat>
{
}

class DogOwner : AnimalOwner<Dog>
{
}

OR

Вы можете отобразить DogOwners и CatOwners в отдельных таблицах и определить конкретный тип животных в отображении.

<class name="CatOwner">
  <!-- ... -->
  <property name="OwnedAninal" class="Cat"/>
</class>
<class name="DogOwner">
  <!-- ... -->
  <property name="OwnedAninal" class="Dog"/>
</class>

OR

Вы немного повозились с NHibernate, как предложено в этом блоге . NH на самом деле может вернуть реальный объект за прокси. Здесь немного более простая реализация, как здесь предлагается:

    public static T CastEntity<T>(this object entity) where T: class
    {
        var proxy = entity as INHibernateProxy;
        if (proxy != null)
        {
            return proxy.HibernateLazyInitializer.GetImplementation() as T;
        }
        else
        {
            return entity as T;
        }
    }

, который можно использовать так:

Dog dog = dogOwner.OwnedAnimal.CastEntit<Dog>();
12 голосов
/ 20 апреля 2009

Я думаю, что у нас недавно была похожая проблема, решение AFAIR состояло в том, чтобы дать «Животному» само- «метод / свойство»:

public Animal Self { get { return this; } }

Это можно затем привести к исправлению "животного". Что происходит, так это то, что ваш исходный объект имеет ссылку на прокси-объект nhibernate (когда он загружен лениво), который действует как Animal для всех методов, предоставляемых через класс Animal (он передает все вызовы загруженному объекту). Однако его нельзя кастовать как любого другого вашего животного, потому что оно не является ни одним из них, оно только эмулирует класс животных. Однако класс, инкапсулированный AnimalProxy, может быть преобразован в подклассическое животное, потому что это реальный экземпляр правильного класса, вам нужно только получить его this ссылку.

1 голос
/ 31 августа 2015

Вы можете попробовать поместить этот метод на вашу базовую сущность:

public virtual T As<T>() where T : Entity {
      return this as T;
}
0 голосов
/ 16 июня 2014

Если вы используете Fluent NHibernate, вы можете использовать переопределение автоматического сопоставления, чтобы отключить отложенную загрузку только для этого свойства:

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner>
{
    public void Override( AutoMapping<DogOwner> mapping )
    {
        mapping.References( x => x.OwnedAnimal ).Not.LazyLoad();
    }
}
0 голосов
/ 21 июля 2010

Если мы работали с той же проблемой, проблема в том, что сгенерированный прокси-сервер является прокси-сервером Animal, а не Dog.

Решение, которое мы использовали, состояло в том, чтобы перезагрузить объект:

Dog dog = this.CurrentSession.Load<Dog>(owner.OwnedAnimal.AnimalID);

Это возвращает вас к сеансу и перезагружает объект с правильным типом.

Надеюсь, это поможет

0 голосов
/ 18 февраля 2009

Вы можете попробовать это, чтобы увидеть тип прокси (при условии, что NH 2.0 +):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass

Но этот тип кастинга или "приглядывания" все равно очень плохая практика ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...