views:

604

answers:

5

I'm not an NHibernate user; I write a serialization utility library. A user has logged a feature-request that I should handle NHibernate proxy classes, treating them the same as the actual type. At the moment my code is treating them as unexpected inheritance, and throwing an exception.

The code won't know in advance about NHibernate (including no reference, but I'm not aftaid of reflection ;-p)

Is there a robust / guaranteed way of detecting such proxy types? Apparently DataContractSerializer handles this fine, so I'm hoping it is something pretty simple. Perhaps some interface or [attribute] decoration.

Also, during deserialization; at the moment I would be creating the original type (not the NHibernate type). Is this fine for persistence purposes? Or is the proxy type required? If the latter; what is required to create an instance of the proxy type?

+1  A: 

Hi Marc.

NHibernate creates (proxy) sub classes of the original entities at runtime to be able to do lazy loading. It is only able to do this because you are forced to mark all properties as "virtual". I can't think of how you could detect that an object is a proxy as opposed to any other kind of sub classing - certainly not in a generic way. I can only presume that your code is throwing an exception in this case because the actual class that is being (de-)serialized isn't marked as being serializable. I think the only thing you can do is loosen up your validation or allow serialization of a sub class if it overrides all properties of its base class.

Deserializing to the original type will be fine.

s1mm0t
(since the serializer is mine, I know it doesn't relate to "serializable", but some valuable points - thanks)
Marc Gravell
I assumed you must have your own "serializable" attribute or some sort of config which you used to identify objects that can be serialized with your serializer.If you're happy to have NHibernate specific code in your serializer, then @Vijay and @Diego's solutions should work for you.
s1mm0t
+1  A: 

NHibernate 2.1+ allows the dynamic proxy provider to be set through configuration. The implementations that I'm aware of are Castle (default), LinFu, and Spring. NHibernate does not require an interface or attribute. I think this makes it fairly impossible to reliably detect if an object is a proxy.

As s1mm0t answered, creating the actual type on deserialization is fine.

Jamie Ide
+6  A: 

You can detect if a class is a NHibernate proxy by casting it to (unsurprisingly) INHibernateProxy.

If you need to get the underlying "real" object, use:

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

You don't need to test for proxies to call Unproxy; it returns the original parameter if it's not a proxy.

Edit: I now use a different approach to get the underlying object, mostly to work around lazy loading and inheritance: http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded-inheritance.html

Diego Mijelshon
Thanks @Diego; that will probably be it; I'll give this a whirl and get back.
Marc Gravell
That led me in the right direction. It isn't *quite* how I solved it in the end (due to not wanting the dependency), but very helpful, thanks.
Marc Gravell
+1  A: 

I'm guessing that you don't really want to access the actual Nhibernate session. This code might better fit your needs:

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

Hope that helps.

Vijay Patel
I expect you mean `return proxy` at the end there - but yes, I'd already concluded something similar by looking at how `PersistenceContext.Unproxy` works. Thanks.
Marc Gravell
No, the last return statement is correct. If you provide an NH proxy object, it returns the known class type. If you provide a regular (ie. non-proxy) object, it returns the object's type.
Vijay Patel
A: 

If you're writing a generic serialization utility library, I don't think you should handle that specific case at all. Your code should not have a dependency on NHibernate. What you should do is provide hooks that the client code can use to influence the operation of your library.

Jordão
@Jordão - a valid point, but the problem is that most client code *also* doesn't want to have to know about this. *Somebody* has to do it; might as well do it once only. And besides, I can do it without actually needing a dependency on NHibernate.
Marc Gravell