I'm not an NHibernate user; I write a serialization utility library. A user has logged a feature-request that I should hand开发者_如何学Pythonle 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?
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
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.
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
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.
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.
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.
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...
精彩评论