Identificación de clases de proxy NHibernate


No soy un usuario de NHibernate; escribo una biblioteca de utilidades de serialización. Un usuario ha registrado una solicitud de característica para que yo maneje las clases proxy de NHibernate, tratándolas de la misma manera que el tipo real. Por el momento mi código los está tratando como herencia inesperada, y lanzando una excepción.

El código no sabrá de antemano sobre NHibernate (incluyendo ninguna referencia, pero no estoy a favor de la reflexión ;-p)

¿Hay una forma robusta / garantizada de detectar tales tipos de proxy? Aparentemente DataContractSerializer maneja esto bien, así que espero que sea algo bastante simple. Tal vez alguna interfaz o [attribute] decoración.

También, durante la deserialización; en este momento estaría creando el tipo original (no el tipo NHibernate). ¿Esto está bien para propósitos de persistencia? ¿O es necesario el tipo de proxy? Si es el último; ¿qué se requiere para crear una instancia del tipo proxy?

Author: Marc Gravell, 2010-04-19

7 answers

Puede detectar si una clase es un proxy NHibernate enviándolo a (como era de esperar) INHibernateProxy.

Si necesita obtener el objeto "real" subyacente, use:

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

No es necesario probar los proxies para llamar a Unproxy; devuelve el parámetro original si no es un proxy.

Editar: Ahora uso un enfoque diferente para obtener el objeto subyacente, principalmente para trabajar alrededor de la carga perezosa y la herencia: http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded-inheritance.html

 49
Author: Diego Mijelshon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-18 02:08:57

Supongo que realmente no quieres acceder a la sesión real de Nhibernate. Este código podría ajustarse mejor a sus necesidades:

/// <summary>
/// Returns the real type of the given proxy. If the object is not a proxy, it's 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();
    }
}

Espero que eso ayude.

 14
Author: Vijay Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-19 11:12:49

Hay una herramienta de NHibernate donde se da un tipo (proxy o no) y devuelve el tipo real. (Estoy usando NHibernate 3)

Así es como lo usas:

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

Espero que esto ayude: D

 8
Author: Samuel Poirier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-28 15:40:37

NHibernate crea subclases (proxy) de las entidades originales en tiempo de ejecución para poder realizar la carga perezosa. Solo puede hacer esto porque se ve obligado a marcar todas las propiedades como "virtuales". No puedo pensar en cómo se podría detectar que un objeto es un proxy en lugar de cualquier otro tipo de subclasificación - ciertamente no de una manera genérica. Solo puedo suponer que su código está lanzando una excepción en este caso porque la clase real que está siendo (des-)serializada no está marcada como Serializable. Creo que lo único que puede hacer es aflojar su validación o permitir la serialización de una subclase si anula todas las propiedades de su clase base.

Deserializar al tipo original estará bien.

 1
Author: s1mm0t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-18 22:57:00

NHibernate 2.1+ permite establecer el proveedor de proxy dinámico a través de la configuración. Las implementaciones que conozco son Castle (por defecto), LinFu y Spring. NHibernate no requiere una interfaz o atributo. Creo que esto hace que sea bastante imposible detectar de manera confiable si un objeto es un proxy.

Como s1mm0t respondió, crear el tipo real en deserialización está bien.

 1
Author: Jamie Ide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-18 23:12:34

Si está escribiendo una biblioteca de utilidad de serialización genérica, no creo que deba manejar ese caso específico en absoluto. Su código no debe tener una dependencia de NHibernate. Lo que debe hacer es proporcionar hooks que el código del cliente pueda usar para influir en el funcionamiento de su biblioteca.

 1
Author: Jordão,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-21 13:00:09

El método de Diego carga completamente el objeto antes de intentar determinar su tipo - este es definitivamente el enfoque más genérico, pero es posible que no necesite cargar el objeto si el tipo concreto ya es conocido por el Proxy.

El método de Vijay es más rápido en los casos en que el tipo concreto se conoce de antemano - pero como señaló Diego, en algunos casos, esa información no está disponible, porque la información requerida para determinar el tipo exacto aún no ha sido cargar.

Teniendo en cuenta ambos escenarios, se me ocurrió lo siguiente:

Https://gist.github.com/1089489

La pieza interesante es esta:

        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();

Esto comprueba primero si el tipo detrás del Proxy es abstracto, y utiliza el método de Vijay o de Diego, dependiendo de si se conoce el tipo concreto.

En otras palabras, solo carga el objeto si el tipo detrás del Proxy no es un tipo concreto, o puede ser de un subtipo.

Hice un prueba unitaria rápida para demostrar que esto funciona, pero no imagino que haya probado todos los casos posibles-creo que la idea es sólida, pero me gustaría escuchar comentarios de Diego y Vijay, u otros.

Gracias!

EDIT: Publicó una actualización al gist anterior, con un método genérico adicional que se puede usar para Desproxy() una Entidad - hay algunos casos en los que puede tener que hacerlo...

 1
Author: mindplay.dk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-26 19:38:54