我正在为我的模型使用Entity Framework,我需要将它们序列化为JSON。问题是EF包含了所有这些非常好的导航集合(例如我的用户模型上有一个Orders属性),当我去序列化这些对象时,序列化程序试图获取这些集合的值,EF对我大喊大叫使用已处置的上下文
ObjectContext实例已被释放,不能再用于需要连接的操作。
我知道我可以使用[ScriptIgnore]来装饰我的属性,以使序列化器不再使用它们,但这就是EF的问题,因为它会为这些属性生成代码。
有没有办法让序列化程序不序列化通用类型EntityCollection<>的属性?
或者有一种方法可以使用另一个强大的json库,如JSON.Net吗?
答案 0 :(得分:8)
您可以声明自定义合约解析程序,指明要忽略的属性。这是一个基于the answer I found here的通用“可忽略”:
/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties. See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
protected readonly Dictionary<Type, HashSet<string>> Ignores;
public IgnorableSerializerContractResolver() {
this.Ignores = new Dictionary<Type, HashSet<string>>();
}
/// <summary>
/// Explicitly ignore the given property(s) for the given type
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName">one or more properties to ignore. Leave empty to ignore the type entirely.</param>
public void Ignore(Type type, params string[] propertyName) {
// start bucket if DNE
if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();
foreach (var prop in propertyName) {
this.Ignores[type].Add(prop);
}
}
/// <summary>
/// Is the given property for the given type ignored?
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public bool IsIgnored(Type type, string propertyName) {
if (!this.Ignores.ContainsKey(type)) return false;
// if no properties provided, ignore the type entirely
if (this.Ignores[type].Count == 0) return true;
return this.Ignores[type].Contains(propertyName);
}
/// <summary>
/// The decision logic goes here
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (this.IsIgnored(property.DeclaringType, property.PropertyName)) {
property.ShouldSerialize = instance => { return false; };
}
return property;
}
}
用法:
var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
答案 1 :(得分:3)
如果想法只是将这些对象返回给客户端,为什么不使用匿名类返回所需内容?
假设你有这个丑陋的EntityFrameworkClass对象列表,你可以这样做:
var result = (from c in List<EntityFrameworkClass>
select new {
PropertyINeedOne=c.EntityFrameworkClassProperty1,
PropertyINeedTwo=c.EntityFrameworkClassProperty2
}).ToList();
答案 2 :(得分:0)
这是我的小贡献。对@drzaus的一些更改回答。 说明:启用了一些重新更改和Fluent。还有一点修复使用PropertyType而不是DeclaringType。
public class IgnorableSerializerContractResolver : DefaultContractResolver
{
protected readonly Dictionary<Type, HashSet<string>> Ignores;
public IgnorableSerializerContractResolver()
{
Ignores = new Dictionary<Type, HashSet<string>>();
}
/// <summary>
/// Explicitly ignore the given property(s) for the given type
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName">one or more properties to ignore. Leave empty to ignore the type entirely.</param>
public IgnorableSerializerContractResolver Ignore(Type type, params string[] propertyName)
{
// start bucket if DNE
if (!Ignores.ContainsKey(type))
Ignores[type] = new HashSet<string>();
foreach (var prop in propertyName)
{
Ignores[type].Add(prop);
}
return this;
}
/// <summary>
/// Is the given property for the given type ignored?
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public bool IsIgnored(Type type, string propertyName)
{
if (!Ignores.ContainsKey(type)) return false;
// if no properties provided, ignore the type entirely
return Ignores[type].Count == 0 || Ignores[type].Contains(propertyName);
}
/// <summary>
/// The decision logic goes here
/// </summary>
/// <param name="member"></param>
/// <param name="memberSerialization"></param>
/// <returns></returns>
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.PropertyType, property.PropertyName))
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
用法:
// Ignore by type, regardless property name
var jsonResolver = new IgnorableSerializerContractResolver().Ignore(typeof(PropertyName))
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
答案 3 :(得分:0)
在@drzaus's answer的基础上,我修改了IsIgnored
方法以使其更通用-
public bool IsIgnored(Type type, string propertyName)
{
var ignoredType = this.Ignores.Keys.FirstOrDefault(t => type.IsSubclassOf(t) || type == t || t.IsAssignableFrom(type));
//if (!this.Ignores.ContainsKey(type)) return false;
if (ignoredType == null) return false;
// if no properties provided, ignore the type entirely
if (this.Ignores[ignoredType].Count == 0) return true;
return this.Ignores[ignoredType].Contains(propertyName);
}
及其用法:
var jsonResolver = new IgnorableSerializerContractResolver();
jsonResolver.Ignore(typeof(BaseClassOrInterface), "MyProperty");
以上内容帮助我制作了一个通用的序列化程序,用于将Thrift对象转换为标准JSON。在序列化实现__isset
接口的类的对象时,我想忽略Thrift.Protocol.TBase
属性。
希望有帮助。
PS-我知道将Thrift对象转换为标准JSON不能达到其目的,但这是与旧版系统接口的要求。
答案 4 :(得分:-2)
如果使用JSON.NET,则可以使用JsonIgnore等属性来忽略某些属性。在序列化通过NHibernate加载的对象时,我使用此功能。
我认为也有可能添加约定。也许你可以为你的EF属性实现一个过滤器。