Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
278 views
in Technique[技术] by (71.8m points)

c# - Getting proxies of the correct type in NHibernate

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

  • I can't change the domain model, the model is handed to me by another department, I tried to get them to change the model and failed.
  • The actual model is much more complicated then the example and the classes have many references between them, using eager loading or adding joins to the queries is out of the question for performance reasons.
  • I have full control of the source code, the hbm mapping and the database schema and I can change them any way I want (as long as I don't change the relationships between the model classes).
  • I have many methods like the one in the example and I don't want to modify all of them.

Thanks,
Nir

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...