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