JSON.Net Serialization of NHibernate Proxies (NH 3.3.2.4000)

I'm still having difficulty with Json.Net and NHibernate to play together. Namely, in getting Json.NET to serialize a proxied NHibernate object.

I followed the recommendations here , both for the accepted answer and for corrections, but not for the bones.

The biggest problem with the above solution is that, apparently, modern versions of NHibernate use the INHibernateProxyProxy interface to create a proxy (and not INHibernateProxy? Can anyone confirm this?), The base class of which in my case is NHibernate.Proxy.DynamicProxy.ProxyDummy , which shows nothing about the base object when I try to create a Json constract using my custom scontract resolver, i.e.

  protected override JsonContract CreateContract(Type objectType) { if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType)) return base.CreateContract(objectType.BaseType); else return base.CreateContract(objectType); } 

Does anyone have any tips for an effective solution to INHibernateProxyProxy ?

+4
source share
3 answers

Found. The original type is accessible via .GetInterfaces() , .GetInterfaces() .:

  protected override JsonContract CreateContract(Type objectType) { if (typeof (INHibernateProxy).IsAssignableFrom(objectType)) { var oType = objectType.GetInterfaces().FirstOrDefault(i => i.FullName.StartsWith("Your.Domain.Namespace")); return oType != null ? base.CreateContract(oType) : base.CreateContract(objectType.BaseType); } return base.CreateContract(objectType); } 
+1
source

Complete solution:

In Global.asax.cs:

  //Define Formatters var formatters = GlobalConfiguration.Configuration.Formatters; var jsonFormatter = formatters.JsonFormatter; var settings = jsonFormatter.SerializerSettings; settings.Formatting = Formatting.Indented; jsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize; jsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; jsonFormatter.SerializerSettings.ContractResolver = new NHibernateContractResolver(); //------------// 

And user contract:

 public class NHibernateContractResolver : DefaultContractResolver { private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers(); protected override List<MemberInfo> GetSerializableMembers(Type objectType) { var members = base.GetSerializableMembers(objectType); members.RemoveAll(memberInfo => (IsMemberPartOfNHibernateProxyInterface(memberInfo)) || (IsMemberDynamicProxyMixin(memberInfo)) || (IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) || (IsMemberInheritedFromProxySuperclass(memberInfo, objectType))); var actualMemberInfos = new List<MemberInfo>(); foreach (var memberInfo in members) { var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name); actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]); } return actualMemberInfos; } private static bool IsMemberDynamicProxyMixin(MemberInfo memberInfo) { return memberInfo.Name == "__interceptors"; } private static bool IsMemberInheritedFromProxySuperclass(MemberInfo memberInfo, Type objectType) { return memberInfo.DeclaringType.Assembly == typeof(INHibernateProxy).Assembly; } private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType) { var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType) ? objectType.BaseType.GetMember(memberInfo.Name) : objectType.GetMember(memberInfo.Name); return infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0; } private static bool IsMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo) { return Array.Exists(NHibernateProxyInterfaceMembers, mi => memberInfo.Name == mi.Name); } protected override JsonContract CreateContract(Type objectType) { if (typeof(INHibernateProxy).IsAssignableFrom(objectType)) { var oType = objectType.GetInterfaces().FirstOrDefault(i => i.FullName.StartsWith("Your.Domain.Namespace")); return oType != null ? base.CreateContract(oType) : base.CreateContract(objectType.BaseType); } return base.CreateContract(objectType); } 

Remember to replace: "Your.Domain.Namespace"

+2
source

From now on, but I think the problem here is that the proxy was not initialized before trying to serialize it.

You must call NHibernateUtil.Initialize(aPersistentObject.LazyProperty); to initialize the proxy object.

Then BaseType probably be correct ... i.e. not ProxyDummy , but the actual type is needed.

For me, the solution looks something like this:

 namespace com.example.DataAccess { public static class Helper { // the implementation I use creates a ThreadStatic ISession, // and then orphans and disposes that ISession when the // result of this method is disposed. public static IDisposable GetSession(); public static Type GetUnproxiedType(Type objectType) { if (typeof(INhibernateProxy).IsAssignableFrom(objectType)) return objectType.BaseType; return objectType; } public static void Initialize(object proxy) { NHibernateUtil.Initialize(proxy); } } } namespace com.example.WebService { internal static class Helper { private class ProxyResolver : CamelCasePropertyNamesContractResolver { protected override JsonContract CreateContract(Type objectType) { return base.CreateContract(DataAccess.Helper.GetUnproxiedType(objectType)); } } public static readonly JsonSerializer serializer = new JsonSerializer { ContractResolver = new ProxyResolver(), Converters = { new StringEnumConverter(), }, DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, Formatting = Formatting.Indented, // etc. }; } } 

So, when I have a REST service endpoint with a proxy type, it looks something like this:

 [WebGet(UriTemplate= "foo/{id}/bar")] public Bar GetFooBar(string id) { using (DataAccess.Helper.GetSession()) { var foo = GetFoo(id); if (foo == null) return null; DataAccess.Helper.Initialize(foo.Bar); return foo.Bar; } } 

and the serializer defined in WebService.Helper is used to serialize the result.

Please note that if the serialization process happens outside your method (as for me), you always need to call the object initialization before you serialize it. You can do this using Global.asax events, but I just process them directly in my service methods.

+1
source

Source: https://habr.com/ru/post/1496784/


All Articles