38

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?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900

7 Answers7

50

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
  • 52,548
  • 16
  • 116
  • 154
  • Thanks @Diego; that will probably be it; I'll give this a whirl and get back. – Marc Gravell Apr 19 '10 at 04:26
  • 2
    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 Jun 21 '10 at 13:52
  • Marc, how did you end up solving this problem? I'd like to do it without the session dependency as well. – James Nail Dec 30 '10 at 17:13
  • @jrnail23 you can use the implementation described in the link – Diego Mijelshon Dec 30 '10 at 17:49
  • Thanks, Diego... I saw the link before, but I was just curious to know what Marc came up with in as an alternate approach. – James Nail Dec 31 '10 at 02:47
  • This solution isn't optimal, as it requires entities to be fully loaded, which may not be necessary - in my case, I needed the Type and the primary key, both of which are in memory already, I do not need the full object to be loaded. The answer below (by Vijay Patel) in my opinion is the correct answer, as it does not suffer from these side-effects. – mindplay.dk Jul 18 '11 at 13:17
  • @mindplay.dk: you are wrong. Vijay's solution does not work (it returns the base class when using inheritance), because the required information is NOT in memory. – Diego Mijelshon Jul 18 '11 at 13:48
  • @Diego: I ran into that same issue today - you're right, this doesn't always work. I will post an example of my current approach below. – mindplay.dk Jul 26 '11 at 16:11
18

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
  • 17,094
  • 6
  • 31
  • 35
  • 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 Apr 19 '10 at 10:46
  • 1
    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 Apr 19 '10 at 11:11
9

There is a tool from NHibernate where you give a type (proxy or not) and it returns the real type. (I am using NHibernate 3)

This is how you use it:

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

Hope this helps :D

Samuel Poirier
  • 1,240
  • 2
  • 15
  • 30
1

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
  • 6,035
  • 4
  • 38
  • 45
  • (since the serializer is mine, I know it doesn't relate to "serializable", but some valuable points - thanks) – Marc Gravell Apr 19 '10 at 03:49
  • 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 Apr 19 '10 at 13:34
1

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
  • 48,427
  • 16
  • 81
  • 117
1

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
  • 55,340
  • 13
  • 112
  • 144
  • 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 Jun 21 '10 at 13:50
1

Diego's method fully loads the object before attempting to determine it's type - this is definitely the most generic approach, but you may not need to load the object if the concrete type is already known to the Proxy.

Vijay's method is faster in cases where the concrete type is known in advance - but as pointed out by Diego, in some cases, that information is not available, because the information required to determine the exact type has not yet been loaded.

Taking both scenarios into account, I came up with the following:

https://gist.github.com/1089489

The interesting piece is this:

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

This checks first to see if the type behind the Proxy is abstract, and uses Vijay's or Diego's method, depending on whether the concrete type is known.

In other words, it only loads the object if the type behind the Proxy is not a concrete type, or may be of a sub-type.

I did a quick unit test to demonstrate that this works, but I don't imagine I've tested all possible cases - I believe the idea is sound, but I would like to hear comments from Diego and Vijay, or others.

Thanks!

EDIT: Posted an update to the gist above, with an additional generic method that can be used to Unproxy() an Entity - there are some cases where you may have to do that...

mindplay.dk
  • 7,085
  • 3
  • 44
  • 54
  • 1
    I guess it works, but the use case for something like this is probably bad OOP (ie. checking the type of an object as part of the regular flow). What are you using it for? My recommended approach is currently [this](http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded-inheritance.html), which *does* force loading, in a much cleaner way. – Diego Mijelshon Jul 26 '11 at 17:39
  • I used it in a few cases where reflective meta-programming can save me a considerable amount of repetition - in the particular case I'm dealing with, I have 5 subtypes of a parent type, which all need identical handling 90% of the time; I needed a bit of reflection to handle the last 10% edge cases conditionally, based on the class hierarchy... – mindplay.dk Jul 26 '11 at 18:09
  • Instead, you could rely on polymorphism and move the behavior to the entity (granted, this is not always possible) – Diego Mijelshon Jul 26 '11 at 18:26
  • I don't like the idea of adding behavior to the entity that exists solely to "please" the ORM or work around it's issues. I prefer extension-methods, which are just syntactic sugar for static method-calls; I prefer to have the ORM-gunk encapsulated in just this one class, and it would be easy to replace if I had to do that... – mindplay.dk Jul 26 '11 at 19:37