NHibernate代理的JSON.Net序列化(NH 3.3.2.4000)

时间:2013-08-13 11:50:24

标签: nhibernate proxy json.net

仍然让Json.Net和NHibernate一起玩得很好。即,让Json.NET序列化代理的NHibernate对象。

我已遵循建议here,既接受了答案,又接受了修正案,但没有骰子。

上述解决方案的最大问题是,NHibernate的现代版本似乎正在使用INHibernateProxyProxy接口来创建代理(而不是INHibernateProxy?还有其他人可以确认吗?),在我的情况下它的基类是NHibernate.Proxy.DynamicProxy.ProxyDummy,当我尝试使用我的自定义scontract解析器创建Json constract时,它没有透露底层对象,即:

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

有没有人对如何有效处理INHibernateProxyProxy有任何建议?

4 个答案:

答案 0 :(得分:2)

完整的解决方案:

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

自定义合同:

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

不要忘记更换:&#34; Your.Domain.Namespace&#34;

答案 1 :(得分:1)

找到它。原始类型可通过.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);
    }

答案 2 :(得分:1)

从前不久,但我认为这里的问题实际上是代理在尝试序列化之前没有初始化。

您必须调用NHibernateUtil.Initialize(aPersistentObject.LazyProperty);来初始化代理对象。

之后,BaseType可能是正确的......即不是ProxyDummy,而是实际需要的类型。

对我来说,解决方案是这样的:

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.
        };
    }
}

因此,当我有一个代理类型的REST服务端点时,它看起来像这样:

[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;
    }
}

并且WebService.Helper中定义的序列化程序用于序列化结果。

请注意,如果序列化过程发生在您的方法之外(就像我一样),您将始终需要在实际序列化之前调用初始化对象。您可以使用Global.asax事件执行此操作,但我只是直接在我的服务方法中处理它。

答案 3 :(得分:0)

我的问题是Newtonsoft.JSON不能解决所有子列表。

我添加了合同解析器

public class ProjectContractResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type objectType)
    {
        if (typeof(INHibernateProxy).IsAssignableFrom(objectType))
        {
            if (objectType.FullName.Equals("DeviceModelProxy"))
                return CreateContract(typeof(DeviceModel));

            if (objectType.FullName.Equals("JobModelProxy"))
                return CreateContract(typeof(JobModel));

            base.CreateContract(objectType.BaseType);
        }
        return base.CreateContract(objectType);
    }
}

在序列化时,添加自定义合同解析器

JsonConvert.SerializeObject(backupDataModel, Formatting.Indented, new JsonSerializerSettings()
        {
            ContractResolver = new ProjectContractResolver()
        });

这解决了我的问题。