2009-01-05 12 views
19

que tienen un problema con los proxies sin inicializar en nhibernateObtención de proxies del tipo correcto en NHibernate

El modelo de dominio

Digamos que tengo dos jerarquías de clases paralelas: animal, perro, gato y AnimalOwner, DogOwner, CatOwner donde Dog y Cat heredan de Animal y DogOwner y CatOwner heredan de AnimalOwner. AnimalOwner tiene una referencia de tipo Animal llamado OwnedAnimal.

Aquí están las clases en el ejemplo:

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 
} 

Las clases tienen mapeo nhibernate adecuada, todas las propiedades son persistentes y todo lo que puede ser cargado ligeramente es cargado ligeramente.

La lógica de negocios de la aplicación solo le permite establecer un Perro en un Propietario de Perro y un Gato en un Propietario de Producto.

El problema

tengo código como este:

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

Este método puede ser llamado por muchos métodos diffrent, en la mayoría de los casos el perro ya está en la memoria y todo está bien, pero raramente el perro no está ya en la memoria, en este caso obtengo un nhibernate "proxy no inicializado" pero el elenco arroja una excepción porque nhibernate genrates un proxy para Animal y no para Dog.

Entiendo que así es como funciona nhibernate, pero necesito saber el tipo sin cargar el objeto o, más correctamente, necesito que el proxy no inicializado sea un proxy de Cat o Dog y no un proxy de Animal.

Restricciones

  • No puedo cambiar el modelo de dominio, el modelo es entregado a mí por otro departamento, traté de conseguir que se cambie el modelo y han fracasado.
  • El modelo real es mucho más complicado que el ejemplo y las clases tienen muchas referencias entre ellas, usar carga ansiosa o agregar combinaciones a las consultas está fuera de cuestión por razones de rendimiento.
  • Tengo control total del código fuente, el mapeo hbm y el esquema de la base de datos y puedo cambiarlos como yo quiera (siempre que no cambie las relaciones entre las clases de modelo).
  • Tengo muchos métodos como el del ejemplo y no quiero modificarlos todos.

Gracias,
Nir

Respuesta

24

Es más fácil desactivar la carga diferida para la clase animal. Dices que está principalmente en la memoria de todos modos.

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

Como una variante de esto, también se podría utilizar no-proxy, ver this post:

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

Por lo que yo puedo ver, sólo funciona cuando el AnimalOwner realidad es un proxy.

O

Puede utilizar los genéricos en el propietario del animal para hacer la referencia de una clase concreta.

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

class CatOwner : AnimalOwner<Cat> 
{ 
} 

class DogOwner : AnimalOwner<Dog> 
{ 
} 

O

puede asignar el DogOwners y CatOwners en tablas separadas, y definir el tipo de animal concreta en la asignación.

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

O

Usted lío un poco alrededor con NHibernate, como se propone en this blog. NH es realmente capaz de devolver el objeto real detrás del proxy. Aquí un poco más simple aplicación tal como se propone allí:

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; 
     } 
    } 

que puede ser utilizado como esto:

Dog dog = dogOwner.OwnedAnimal.CastEntit<Dog>(); 
+0

Gracias, no estoy seguro de que pueda usar esas técnicas en mi caso, pero las voy a verificar. – Nir

+0

Agregado a otras opciones ('no-proxy' y' CastEntity'). –

+0

CastEntity fue un buen intento, pero hay un problema con el que me estoy encontrando: si tienes clases principales y secundarias, y la instancia "p" del tipo Parent es realmente un elemento secundario (polimorfismo), el elenco falla el tiempo de compilación aunque lo haría trabajo en tiempo de ejecución. También tenga en cuenta que su primer "INhibernateProxy" carece de un capital en la "h". Cambiar a un "como" no ayudó, pero el truco del "Ser" funcionó. –

0

Es posible que desee probar este para ver el tipo de proxy (suponiendo NH 2.0+):

((INHibernateProxy)proxy).HibernateLazyInitializer.PersistentClass 

Pero este tipo de fundición o "tipo Echar un vistazo" es una práctica muy mala de todos modos ...

12

Creo que recientemente hemos tenido un problema similar, solución AFAIR era dar 'Animal' una auto - "método/propiedad":

public Animal Self { get { return this; } } 

Esto podría ser cambiado para corregir "animal". Lo que sucede es que su objeto original tiene una referencia al objeto proxy nhibernate (cuando está cargado de forma perezosa), que actúa como Animal para todos los métodos expuestos a través de la clase Animal (pasa todas las llamadas al objeto cargado). Sin embargo, no se puede convertir como cualquiera de tus otros animales porque no es ninguno de estos, solo emula la clase Animal. Sin embargo, la clase que está encapsulada por AnimalProxy puede convertirse en un animal subclasificado porque es una instancia real de la clase correcta, solo necesita llegar a su referencia this.

+0

http://stackoverflow.com/q/3523686/189429 –

0

Si hemos estado trabajando con el mismo problema, el problema es que el proxy generado es el proxy de Animal en lugar de Dog.

La solución que utilizamos fue para recargar el objeto:

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

Esto se remonta a la sesión y vuelve a cargar el objeto con el tipo correcto.

Esperanza esto ayuda

+0

Esto realmente no carga el objeto nuevamente, a menos que haya creado una nueva sesión mientras tanto, lo que no es recomendable. NH le asegura que obtendrá la misma instancia en la memoria para la misma instancia en la base de datos durante toda la vida de la sesión. –

+0

En realidad, volvimos a la solución 'Self' anterior, ya que consideramos que es más confiable a pesar de que este es el trabajo ofrecido en nuestro libro. – Liath

0

Si utiliza Fluido NHibernate, puede utilizar una anulación de asignación automática para desactivar la carga diferida por sólo esa propiedad:

public class DogOwnerMapOverride : IAutoMappingOverride<DogOwner> 
{ 
    public void Override(AutoMapping<DogOwner> mapping) 
    { 
     mapping.References(x => x.OwnedAnimal).Not.LazyLoad(); 
    } 
} 
-1

Usted puede tratar de poner este método en su entidad base:

public virtual T As<T>() where T : Entity { 
     return this as T; 
} 
Cuestiones relacionadas