有人在使用JSON.NET和nHibernate吗?我注意到当我尝试加载带有子集合的类时,我遇到了错误。
答案 0 :(得分:42)
我遇到了同样的问题,所以我尝试使用@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);
}
}
答案 1 :(得分:24)
我们遇到了这个确切的问题,这是在Handcraftsman的回应中得到了解决的。
问题源于JSON.NET对如何序列化NHibernate的代理类感到困惑。解决方案:将代理实例序列化为基类。
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,我会尝试更新代码。
答案 2 :(得分:18)
我将NHibernate与Json.NET一起使用,并注意到我在序列化对象中遇到了无法理解的“__interceptors”属性。 Lee Henson进行了一次谷歌搜索this excellent solution,我改编自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属性中放置一个实例。可以通过将ReferenceLoopHandling属性设置为ReferenceLoopHandling.Ignore来解决jishi指出的循环依赖问题。这是一个扩展方法,可用于使用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);
}
}
}
答案 3 :(得分:3)
您是否收到循环依赖错误?如何忽略序列化中的对象?
由于延迟加载会生成代理对象,因此类成员拥有的任何属性都将丢失。我遇到了与Newtonsoft JSON-serializer相同的问题,因为代理对象不再具有[JsonIgnore]属性。
答案 4 :(得分:3)
您可能希望加载大部分对象,以便对其进行序列化:
ICriteria ic = _session.CreateCriteria(typeof(Person));
ic.Add(Restrictions.Eq("Id", id));
if (fetchEager)
{
ic.SetFetchMode("Person", FetchMode.Eager);
}
一个很好的方法是在数据提供者方法的构造函数(bool isFetchEager)中添加一个bool。
答案 5 :(得分:1)
我认为这是我认为的设计问题。因为NH在所有数据库下建立了连接并且在中间有代理,所以直接序列化它们对应用程序的透明性是不利的(并且你可以看到Json.NET根本不喜欢它们)。
您不应该自己序列化实体,但是您应该将它们转换为&#34; view&#34;对象或POCO或DTO对象(无论你想要什么),然后序列化它们。
不同之处在于,NH实体可能具有代理,延迟属性等。视图对象是简单对象,只有基元,默认情况下可序列化。
如何管理FK? 我个人的规则是:
实体级别:人员类和关联的性别类
查看级别:具有GenderId和GenderName属性的人员视图。
这意味着您需要在转换为视图对象时将属性扩展为基元。这样,您的json对象也更简单,更容易处理。
当您需要将更改推送到数据库时,在我的情况下,我使用AutoMapper并执行ValueResolver类,它可以将您的新Guid转换为Gender对象。
更新:检查http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/是否可以直接从NH获取视图(AliasToBean)。这将是数据库方面的一个推动力。
答案 6 :(得分:0)
当NHibernate将嵌套的集合属性包装为PersistentGenericBag <>类型时,可能会发生此问题。
GetSerializableMembers和CreateContract替代无法检测到这些嵌套的集合属性被“代理”。解决此问题的一种方法是重写CreateProperty方法。诀窍是使用反射从属性中获取值并测试类型是否为PersistentGenericBag。此方法还可以过滤生成异常的所有属性。
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;
}
}
上面使用的IsSubclassOfRawGeneric扩展名:
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;
}
}
答案 7 :(得分:0)
如果您序列化包含 NHibernate 代理类的对象,您可能最终会下载整个数据库,因为一旦访问该属性,NHibernate 将触发对数据库的请求。 我刚刚为 NHibernate 实现了一个工作单元:NHUnit,它解决了 NHibernate 中两个最烦人的问题:使用 fetch 时的代理类和笛卡尔积。
你会如何使用它?
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
上面的代码基本上是配置一个查询:通过 id 返回一个带有多个子对象的客户,这些子对象应该与其他查询(期货)一起执行,并且返回的结果应该去除 NHibernate 代理。调用 ValueAsync
时执行查询。
NHUnit 决定是否应该加入主查询、创建新的未来查询或使用批量提取。
有一个 simple example project on Github 向您展示如何使用 NHUnit 包。如果其他人对这个项目感兴趣,我会投入更多时间让它变得更好。
答案 8 :(得分:0)
这是我使用的:
IEntity
。我们将使用标记接口来检测合同解析器中的 NHibernate 实体类型。
public class CustomerEntity : IEntity { ... }
为 JSON.NET 创建自定义合同解析器
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;
}
}
null
。[]
空数组。因此,要使延迟加载的属性出现在 json 输出中,您需要在序列化之前在查询或代码中急切加载它。
用法:
JsonConvert.SerializeObject(entityToSerialize, new JsonSerializerSettings() {
ContractResolver = new NHibernateContractResolver()
});
或者全局在 ASP.NET Core Startup 类中
services.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new NHibernateContractResolver();
});
使用: