JSON.NET和nHibernate延迟加载集合
有没有人用nHibernate使用JSON.NET? 我注意到当我尝试使用子集合加载一个类时出现错误。
我面临同样的问题,所以我试图使用@ 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); } }
我们有这个确切的问题,这是从Handcraftsman在这里的回应灵感解决的。
问题出现在JSON.NET被弄糊涂了如何序列化NHibernate的代理类。 解决scheme:序列化代理实例,如其基类。
Handcraftsman的代码简化版本是这样的:
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); } } }
恕我直言,这个代码的优点仍然依赖于JSON.NET的默认行为有关自定义属性等(和代码是短得多!)。
它是这样使用的
var serializer = new JsonSerializer{ ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = new NHibernateContractResolver() }; StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter); serializer.Serialize(jsonWriter, objectToSerialize); string serializedObject = stringWriter.ToString();
注意:这段代码是为NHibernate 2.1编写和使用的。 正如一些评论者指出的那样,NHibernate的后续版本并不适用,你将不得不做一些调整。 我会尝试更新代码,如果我必须使用更高版本的NHibernate。
我使用NHibernate与Json.NET,注意到我在序列化的对象中得到莫名的“__interceptors”属性。 谷歌search出现了Lee Henson 这个优秀的解决scheme ,我改编了Json.NET 3.5 Release 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); } }
要使用它,只需在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); } } }
你是否得到循环依赖性错误? 你如何忽略序列化的对象?
由于延迟加载会生成代理对象,因此您的类成员所拥有的任何属性都将丢失。 我遇到了与Newtonsoft JSON序列化程序相同的问题,因为代理对象不再具有[JsonIgnore]属性。
您可能会想要加载大部分对象,以便可以序列化:
ICriteria ic = _session.CreateCriteria(typeof(Person)); ic.Add(Restrictions.Eq("Id", id)); if (fetchEager) { ic.SetFetchMode("Person", FetchMode.Eager); }
一个很好的方法是将bool添加到数据提供者方法的构造函数(bool isFetchEager)中。
我认为这是一个devise问题。 由于NH连接到数据库的底层,中间有代理服务器,所以直接序列化应用程序的透明性并不是很好(正如你可以看到的,Json.NET根本不喜欢它们)。
你不应该序列化实体本身,但你应该将它们转换成“视图”对象或POCO或DTO对象(无论你想调用它们),然后序列化它们。
不同之处在于,NH实体可能具有代理,惰性属性等。视图对象是仅具有可默认序列化的基元的简单对象。
如何pipe理FK? 我的个人规则是:
实体级别:人员类别和相关的性别类别
查看等级:具有GenderId和GenderName属性的人员视图。
这意味着在转换为视图对象时,需要将属性扩展为基元。 这样,你的json对象也更简单,更容易处理。
当你需要将更改推送到数据库时,在我的情况下,我使用AutoMapper并执行一个ValueResolver类,它可以将新的Guid转换为Gender对象。
更新:检查http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/的方式来从NH直接获取视图(AliasToBean)。; 这将是在数据库方面的一个提升。