JSON.NET和nHibernate延迟加载集合

JSON.NET和nHibernate延迟加载集合,nhibernate,json.net,Nhibernate,Json.net,有人将JSON.NET与nHibernate一起使用吗?我注意到,当我尝试加载包含子集合的类时,会出现错误。是否会出现循环依赖性错误?如何从序列化中忽略对象 由于延迟加载会生成代理对象,因此类成员拥有的任何属性都将丢失。我在Newtonsoft JSON serializer中遇到了同样的问题,因为代理对象不再具有[JsonIgnore]属性。您可能希望加载大部分对象,以便对其进行序列化: ICriteria ic = _session.CreateCriteria(typeof

有人将JSON.NET与nHibernate一起使用吗?我注意到,当我尝试加载包含子集合的类时,会出现错误。

是否会出现循环依赖性错误?如何从序列化中忽略对象


由于延迟加载会生成代理对象,因此类成员拥有的任何属性都将丢失。我在Newtonsoft JSON serializer中遇到了同样的问题,因为代理对象不再具有[JsonIgnore]属性。

您可能希望加载大部分对象,以便对其进行序列化:

        ICriteria ic = _session.CreateCriteria(typeof(Person));

        ic.Add(Restrictions.Eq("Id", id));

        if (fetchEager)
        {
            ic.SetFetchMode("Person", FetchMode.Eager);
        }

一个很好的方法是向数据提供程序方法的构造函数(bool isFetchEager)添加bool。

我将NHibernate与Json.NET一起使用,并注意到我在序列化对象中获得了无法解释的“\uu拦截器”属性。Lee Henson发现了一个google搜索,我将其改编为Json.NET 3.5版本5,如下所示

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);
  }
}
公共类NHibernateContractResolver:DefaultContractResolver
{
private static readonly MemberInfo[]nhibernateproxy接口成员=typeof(INHibernateProxy).GetMembers();
受保护的覆盖列表GetSerializableMembers(类型objectType)
{
var members=base.GetSerializableMembers(objectType);
members.RemoveAll(memberInfo=>
(IsMemberPartOfHibernateProxy接口(memberInfo))||
(IsMemberDynamicProxyMixin(成员信息))||
(IsMemberMarkedWithIgnoreAttribute(memberInfo,objectType))||
(IsMemberInheritedFromProxySuperclass(memberInfo,objectType));
var actualMemberInfos=新列表();
foreach(成员中的var memberInfo)
{
var infos=memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
ActualMemberInfo.Add(infos.Length==0?memberInfo:infos[0]);
}
返回实际成员数;
}
私有静态bool IsMemberDynamicProxyMixin(MemberInfo MemberInfo)
{
returnmemberinfo.Name==“\uuuu拦截器”;
}
私有静态bool IsMemberInheritedFromProxySuberclass(MemberInfo MemberInfo,类型objectType)
{
return memberInfo.DeclaringType.Assembly==typeof(INHibernateProxy).Assembly;
}
私有静态bool IsMemberMarkedWithIgnoreAttribute(MemberInfo MemberInfo,类型objectType)
{
var infos=typeof(INHibernateProxy).IsAssignableFrom(objectType)
?objectType.BaseType.GetMember(memberInfo.Name)
:objectType.GetMember(memberInfo.Name);
返回信息[0]。GetCustomAttributes(typeof(JsonIgnoreAttribute),true)。长度>0;
}
私有静态bool IsMemberPartOfHibernateProxy接口(MemberInfo MemberInfo)
{
返回Array.Exists(nhibernateproxy接口成员,mi=>memberInfo.Name==mi.Name);
}
}
要使用它,只需在JsonSerializer的ContractResolver属性中放置一个实例。jishi指出的循环依赖性问题可以通过将ReferenceLoopHandling属性设置为ReferenceLoopHandling.Ignore来解决。下面是一个扩展方法,可用于使用Json.Net序列化对象

  public static void SerializeToJsonFile<T>(this T itemToSerialize, string filePath)
  {
    using (StreamWriter streamWriter = new StreamWriter(filePath))
    {
      using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
      {
        jsonWriter.Formatting = Formatting.Indented;
        JsonSerializer serializer = new JsonSerializer
          {
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            ContractResolver = new NHibernateContractResolver(),
          };
        serializer.Serialize(jsonWriter, itemToSerialize);
      }
    }
  }
publicstaticvoid SerializeToJsonFile(此T itemToSerialize,stringfilepath)
{
使用(StreamWriter StreamWriter=新StreamWriter(文件路径))
{
使用(JsonWriter JsonWriter=newjsontextwriter(streamWriter))
{
jsonWriter.Formatting=Formatting.Indented;
JsonSerializer serializer=新的JsonSerializer
{
NullValueHandling=NullValueHandling.Ignore,
ReferenceLoopHandling=ReferenceLoopHandling.Ignore,
ContractResolver=新的NHibernateContractResolver(),
};
serializer.Serialize(jsonWriter,itemToSerialize);
}
}
}

我们有一个确切的问题,这个问题是从手工艺人的反应中得到启发而解决的

问题源于JSON.NET对如何序列化NHibernate的代理类感到困惑。解决方案:像基类一样序列化代理实例

手工艺人代码的简化版本如下所示:

public class NHibernateContractResolver : DefaultContractResolver {
    protected override List<MemberInfo> GetSerializableMembers(Type objectType) {
        if (typeof(INHibernateProxy).IsAssignableFrom(objectType)) {
            return base.GetSerializableMembers(objectType.BaseType);
        } else {
            return base.GetSerializableMembers(objectType);
        }
    }
}

注:此代码是为NHibernate2.1编写并与之一起使用的。正如一些评论者指出的那样,对于NHibernate的更高版本来说,它不是开箱即用的,您必须进行一些调整。如果必须使用NHibernate的更高版本,我将尝试更新代码。

我也面临同样的问题,因此我尝试使用@Liedman的代码,但从未为代理引用调用
GetSerializableMembers()
。 我找到了另一个要覆盖的方法:

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

我认为这是一个设计问题。因为NH与数据库下面的数据库连接并在中间有代理,所以应用程序的透明性不利于直接串行化(正如您可以看到Json.NET根本不喜欢它们)。 您不应该序列化实体本身,但应该将它们转换为“视图”对象或POCO或DTO对象(无论您如何称呼它们),然后序列化它们

区别在于NH实体可能有代理、惰性属性等。视图对象是简单的对象,只有原语,默认情况下可以序列化

如何管理FKs? 我个人的原则是:

实体级别:Person类和关联的性别类

视图级别:具有GenderId和GenderName属性的Person视图

这意味着您需要在转换为视图对象时将特性扩展为基本体。这样,json对象也更简单,更易于处理

当您需要将更改推送到DB时,在我的例子中,我使用AutoMapper并执行ValueResolver类,该类可以将您的新Guid转换为性别对象


更新:检查从NH直接获取视图(AliasToBean)的方法。这将对数据库端起到促进作用。

当NHibernate将嵌套的集合属性包装到Pers中时,可能会出现问题
  public class NHibernateContractResolver : DefaultContractResolver
  {
      protected override JsonContract CreateContract(Type objectType)
      {
          if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
              return base.CreateContract(objectType.BaseType);
          else
              return base.CreateContract(objectType);
      }
  }
    public class NHibernateContractResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);

            property.ShouldSerialize = instance =>
            {
                try
                {
                    PropertyInfo prop = (PropertyInfo)member;
                    if (prop.CanRead)
                    {
                        var value = prop.GetValue(instance, null);
                        if (value != null && typeof(NHibernate.Collection.Generic.PersistentGenericBag<>).IsSubclassOfRawGeneric(value.GetType()))
                            return false;

                        return true;
                    }
                }
                catch
                { }
                return false;
            };

            return property;
        }
    }
public static class TypeExtensions
{
    public static bool IsSubclassOfRawGeneric(this Type generic, Type? toCheck)
    {
        while (toCheck != null && toCheck != typeof(object))
        {
            var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
            if (generic == cur)
            {
                return true;
            }
            toCheck = toCheck?.BaseType;
        }
        return false;
    }
}
var customer = await _dbContext.Customers.Get(customerId) //returns a wrapper to configure the query
           .Include(c => c.Addresses.Single().Country, //include Addresses and Country
                    c => c.PhoneNumbers.Single().PhoneNumberType) //include all PhoneNumbers with PhoneNumberType
           .Unproxy() //instructs the framework to strip all the proxy classes when the Value is returned
           .Deferred() //instructs the framework to delay execution (future)
           .ValueAsync(token); //this is where all deferred queries get executed
   public class CustomerEntity : IEntity {    ...   }
    public class NHibernateProxyJsonValueProvider : IValueProvider {

     private readonly IValueProvider _valueProvider;

     public NHibernateProxyJsonValueProvider(IValueProvider valueProvider)
     {
         _valueProvider = valueProvider;
     }

     public void SetValue(object target, object value)
     { 
         _valueProvider.SetValue(target, value); 
     }

     private static (bool isProxy, bool isInitialized) GetProxy(object proxy)
     {
         // this is pretty much what NHibernateUtil.IsInitialized() does.
         switch (proxy)
         {
             case INHibernateProxy hibernateProxy:
                 return (true, !hibernateProxy.HibernateLazyInitializer.IsUninitialized);
             case ILazyInitializedCollection initializedCollection:
                 return (true, initializedCollection.WasInitialized);
             case IPersistentCollection persistentCollection:
                 return (true, persistentCollection.WasInitialized);
             default:
                 return (false, false);
         }
     }

     public object GetValue(object target)
     { 
         object value = _valueProvider.GetValue(target);
         (bool isProxy, bool isInitialized) = GetProxy(value);
         if (isProxy)
         {
             if (isInitialized)
             {
                 return value;
             }

             if (value is IEnumerable)
             {
                 return Enumerable.Empty<object>();
             }

             return null;
         }

         return value;
     }  
}

public class NHibernateContractResolver : CamelCasePropertyNamesContractResolver {

     protected override JsonContract CreateContract(Type objectType)
     {
         if (objectType.IsAssignableTo(typeof(IEntity)))
         {
             return base.CreateObjectContract(objectType);
         }

         return base.CreateContract(objectType);
     } 

     protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
     {
         JsonProperty property = base.CreateProperty(member, memberSerialization);

         property.ValueProvider = new NHibernateProxyJsonValueProvider(property.ValueProvider);

         return property;
     }  
 }
JsonConvert.SerializeObject(entityToSerialize, new JsonSerializerSettings() {
  ContractResolver = new NHibernateContractResolver()
});
   services.AddNewtonsoftJson(options =>
            { 
                options.SerializerSettings.ContractResolver = new NHibernateContractResolver();  
            });