Подтвердить что ты не робот

Идентификация прокси-классов NHibernate

Я не являюсь пользователем NHibernate; Я пишу библиотеку утилиты сериализации. Пользователь зарегистрировал запрос функции, чтобы я работал с прокси-классами NHibernate, рассматривая их так же, как и фактический тип. В настоящий момент мой код обрабатывает их как неожиданное наследование и бросает исключение.

Код не будет знать заранее о NHibernate (включая ссылку, но я не уверен в рефлексии; -p)

Существует ли надежный/гарантированный способ обнаружения таких типов прокси? По-видимому, DataContractSerializer справляется с этим, поэтому я надеюсь, что это что-то довольно простое. Возможно, какой-то интерфейс или [attribute] украшение.

Кроме того, во время десериализации; на данный момент я бы создал оригинальный тип (а не тип NHibernate). Это хорошо для настойчивости? Или требуется тип прокси? Если последний; что требуется для создания экземпляра типа прокси?

4b9b3361

Ответ 1

Вы можете определить, является ли класс прокси-сервером NHibernate, отбросив его (неудивительно) INHibernateProxy.

Если вам нужен базовый "реальный" объект, используйте:

Session.GetSessionImplementation().PersistenceContext.Unproxy(proxiedObject)

Вам не нужно проверять, чтобы прокси-серверы вызывали Unproxy; он возвращает исходный параметр, если он не прокси.

Изменить: Теперь я использую другой подход для получения базового объекта, в основном для работы с ленивой загрузкой и наследованием: http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded-inheritance.html

Ответ 2

Я предполагаю, что вы действительно не хотите получать доступ к фактической сессии Nhibernate. Этот код может лучше соответствовать вашим потребностям:

/// <summary>
/// Returns the real type of the given proxy. If the object is not a proxy, it normal type is returned.
/// </summary>
internal static Type GetRealType(this object proxy)
{
    if (proxy is INHibernateProxy)
    {
        var lazyInitialiser = ((INHibernateProxy)proxy).HibernateLazyInitializer;
        return lazyInitialiser.PersistentClass;
    }
    else
    {
        return proxy.GetType();
    }
}

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

Ответ 3

Существует инструмент из NHibernate, где вы указываете тип (прокси-сервер или нет), и он возвращает реальный тип. (Я использую NHibernate 3)

Вот как вы его используете:

var realType = NHibernate.NHibernateUtil.GetClass(proxyInstance);

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

Ответ 4

NHibernate создает (прокси) под-классы исходных объектов во время выполнения, чтобы выполнять ленивую загрузку. Это возможно только потому, что вы вынуждены отмечать все свойства как "виртуальные". Я не могу придумать, как вы можете обнаружить, что объект является прокси-сервером, а не любым другим подклассом - конечно, не общим. Я могу только предположить, что ваш код генерирует исключение в этом случае, потому что фактический класс, который является (де-) сериализован, не помечен как сериализуемый. Я думаю, что единственное, что вы можете сделать, это ослабить вашу проверку или разрешить сериализацию подкласса, если он переопределяет все свойства его базового класса.

Дессериализация исходного типа будет прекрасной.

Ответ 5

NHibernate 2.1+ позволяет установить динамический прокси-провайдер через конфигурацию. Реализации, о которых я знаю, - это Castle (по умолчанию), LinFu и Spring. NHibernate не требует интерфейса или атрибута. Я думаю, что это делает невозможным надежное обнаружение, является ли объект прокси.

Как ответил s1mm0t, создание фактического типа при десериализации будет прекрасным.

Ответ 6

Если вы пишете универсальную библиотеку утилиты сериализации, я не думаю, что вы должны справиться с этим конкретным случаем вообще. Ваш код не должен иметь зависимости от NHibernate. Что вам нужно сделать, это предоставить перехваты, которые код клиента может использовать для влияния на работу вашей библиотеки.

Ответ 7

Метод Диего полностью загружает объект, прежде чем пытаться определить его тип - это, безусловно, самый общий подход, но вам может не понадобиться загружать объект, если конкретный тип уже известен прокси.

Метод Виджей быстрее в тех случаях, когда конкретный тип известен заранее, но, как указывает Диего, в некоторых случаях эта информация недоступна, поскольку информация, необходимая для определения точного типа, еще не загружена.

Учитывая оба сценария, я придумал следующее:

https://gist.github.com/1089489

Интересная штука такова:

        if (entity is INHibernateProxy)
        {
            var lazyInitialiser = ((INHibernateProxy)entity).HibernateLazyInitializer;
            var type = lazyInitialiser.PersistentClass;

            if (type.IsAbstract || type.GetNestedTypes().Length > 0)
                return Service.Session.GetSessionImplementation().PersistenceContext.Unproxy(entity).GetType();
            else // we don't need to "unbox" the Proxy-object to get the type
                return lazyInitialiser.PersistentClass;
        }

        return entity.GetType();

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

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

Я сделал быстрый unit test, чтобы продемонстрировать, что это работает, но я не думаю, что я проверил все возможные случаи - я считаю, что идея звучит, но я хотел бы услышать комментарии от Диего и Виджей, или другие.

Спасибо!

EDIT: добавлено обновление для gist выше, с дополнительным универсальным методом, который можно использовать для Unproxy() Entity - есть случаи, когда вам, возможно, придется это сделать...