I have a problem with uninitialized proxies in nhibernate
The Domain Model
Let's say I have two parallel class hierarchies: Animal, Dog, Cat and AnimalOwner, DogOwner, CatOwner where Dog and Cat both inherit from Animal and DogOwner and CatOwner both inherit from AnimalOwner. AnimalOwner has a reference of type Animal called OwnedAnimal.
Here are the classes in the example:
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
}
The classes have proper nhibernate mapping, all properties are persistent and everything that can be lazy loaded is lazy loaded.
The application business logic only let you to set a Dog in a DogOwner and a Cat in a CatOwner.
The Problem
I have code like this:
public void ProcessDogOwner(DogOwner owner)
{
Dog dog = (Dog)owner.OwnedAnimal;
....
}
This method can be called by many diffrent methods, in most cases the dog is already in memory and everything is ok, but rarely the dog isn't already in memory - in this case I get an nhibernate "uninitialized proxy" but the cast throws an exception because nhibernate genrates a proxy for Animal and not for Dog.
I understand that this is how nhibernate works, but I need to know the type without loading the object - or, more correctly I need the uninitialized proxy to be a proxy of Cat or Dog and not a proxy of Animal.
Constraints
Thanks,
Nir
It's easiest to turn off lazy loading for the animal class. You say it's mostly in memory anyway.
<class name="Animal" lazy="false">
<!-- ... -->
</class>
As a variant of that, you could also use no-proxy
, see this post:
<property name="OwnedAnimal" lazy="no-proxy"/>
As far as I can see, it only works when the AnimalOwner
actually is a proxy.
OR
You can use generics on the animal owner to make the reference a concrete class.
class AnimalOwner<TAnimal>
{
virtual TAnimal OwnedAnimal {get;set;}
}
class CatOwner : AnimalOwner<Cat>
{
}
class DogOwner : AnimalOwner<Dog>
{
}
OR
You can map the DogOwners
and CatOwners
in separate tables, and define the concrete animal type in the mapping.
<class name="CatOwner">
<!-- ... -->
<property name="OwnedAninal" class="Cat"/>
</class>
<class name="DogOwner">
<!-- ... -->
<property name="OwnedAninal" class="Dog"/>
</class>
OR
You mess a little around with NHibernate, as proposed in this blog. NH is actually able to return the real object behind the proxy. Here a bit simpler implementation as proposed there:
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;
}
}
which can be used like this:
Dog dog = dogOwner.OwnedAnimal.CastEntity<Dog>();
I think we recently had a similar problem, AFAIR solution was to give 'Animal' a self -"method/property":
public Animal Self { get { return this; } }
This could then be casted to correct "animal". What happens is that your original object has a reference to nhibernate proxy object (when it's lazily loaded), which acts as Animal for all methods exposed via Animal class (it passes all calls to the loaded object). However it cannot be casted as any of your other animals because it is none of these, it only emulates the Animal class. However the class that is encapsulated by AnimalProxy can be casted as subclassed animal because it is a real instance of correct class, you only need to get to it's this
reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With