我尝试序列化从实体数据模型.edmx和使用时自动生成的POCO类
JsonConvert.SerializeObject
我收到以下错误:
错误检测到类型为System.data.entity的自引用循环。
我该如何解决这个问题?
async
方法调用(a Task
)的结果而忘记为await
语句加上前缀时,发生了此错误。
我尝试序列化从实体数据模型.edmx和使用时自动生成的POCO类
JsonConvert.SerializeObject
我收到以下错误:
错误检测到类型为System.data.entity的自引用循环。
我该如何解决这个问题?
async
方法调用(a Task
)的结果而忘记为await
语句加上前缀时,发生了此错误。
Answers:
那是最好的解决方案 https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7
(我选择/尝试了这个,还有很多其他选择)
json.net序列化程序可以忽略循环引用。将以下代码放入WebApiConfig.cs
文件中:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
简单的修复将使序列化程序忽略引用,这将导致循环。但是,它有局限性:
如果要在非API ASP.NET项目中使用此修复程序,可以将上述行添加到中Global.asax.cs
,但首先添加:
var config = GlobalConfiguration.Configuration;
如果要在.Net Core项目中使用此功能,可以更改Startup.cs
为:
var mvc = services.AddMvc(options =>
{
...
})
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
此第二个修复程序类似于第一个。只需将代码更改为:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
应用此设置后,数据形状将更改。
[
{
"$id":"1",
"Category":{
"$id":"2",
"Products":[
{
"$id":"3",
"Category":{
"$ref":"2"
},
"Id":2,
"Name":"Yogurt"
},
{
"$ref":"1"
}
],
"Id":1,
"Name":"Diary"
},
"Id":1,
"Name":"Whole Milk"
},
{
"$ref":"3"
}
]
$ id和$ ref保持所有引用并使对象图级别平坦,但是客户端代码需要知道形状变化以使用数据,并且它仅适用于JSON.NET序列化器。
此修复程序修饰模型类上的属性,以控制模型或属性级别上的序列化行为。要忽略该属性:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
JsonIgnore用于JSON.NET,而IgnoreDataMember用于XmlDCSerializer。要保留参考:
// Fix 3
[JsonObject(IsReference = true)]
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
// Fix 3
//[JsonIgnore]
//[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
[DataContract(IsReference = true)]
public class Product
{
[Key]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual Category Category { get; set; }
}
JsonObject(IsReference = true)]
用于JSON.NET和[DataContract(IsReference = true)]
XmlDCSerializer。注意:DataContract
在类上应用后,您需要添加DataMember
到要序列化的属性。
这些属性可以同时应用于json和xml序列化程序,并提供对模型类的更多控制。
[JsonIgnore]
上面的属性对我有用。
ReferenceLoopHandling.Error
(默认)如果遇到参考循环将出错。这就是为什么您会例外。ReferenceLoopHandling.Serialize
如果对象是嵌套的但不是无限期的,则很有用。ReferenceLoopHandling.Ignore
如果对象本身是子对象,则不会序列化该对象。 例:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
如果必须序列化无限期嵌套的对象,则可以使用PreserveObjectReferences来避免StackOverflowException。
例:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings {
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
选择对您要序列化的对象有意义的对象。
ReferenceLoopHandling.Serialize
会导致序列化器进入无限递归循环并溢出堆栈。
解决方法是忽略循环引用,而不是序列化它们。在中指定了此行为JsonSerializerSettings
。
单JsonConvert
重载:
JsonConvert.SerializeObject(YourObject, Formatting.Indented,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
);
使用Application_Start()
Global.asax.cs中的代码进行全局设置:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
最简单的方法是从nuget 安装Json.NET
,并将[JsonIgnore]
属性添加到类中的虚拟属性,例如:
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Project_ID { get; set; }
[JsonIgnore]
public virtual Project Project { get; set; }
尽管这些天来,我创建的模型只包含我要传递的属性,因此它比较轻巧,不包含不需要的集合,并且在重建生成的文件时不会丢失更改...
在.NET Core 1.0中,可以在Startup.cs文件中将此设置为全局设置:
using System.Buffers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
// beginning of Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings(){
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}, ArrayPool<char>.Shared));
});
}
如果您使用的是.NET Core 2.x,请更新Startup.cs中的ConfigureServices部分
https://docs.microsoft.com/zh-cn/ef/core/querying/related-data#related-data-and-serialization
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
如果您使用的是不带MVC的.NET Core 3.x,则可能是:
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
如果您使用的是Entity Framework和数据库优先设计模式,则此参考循环处理几乎是必需的。
services.AddMvc()
怎么办?
在发生循环问题时,要在NEWTONSOFTJSON中序列化我们,就我而言,我不需要修改global.asax或apiconfig。我只是使用JsonSerializesSettings忽略了循环处理。
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var lst = db.shCards.Where(m => m.CardID == id).ToList();
string json = JsonConvert.SerializeObject(lst, jss);
Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
我们可以将这两行添加到DbContext类构造函数中以禁用自我引用循环,例如
public TestContext()
: base("name=TestContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
您也可以将属性应用于属性。该[JsonProperty( ReferenceLoopHandling = ... )]
属性非常适合于此。
例如:
/// <summary>
/// Represents the exception information of an event
/// </summary>
public class ExceptionInfo
{
// ...code omitted for brevity...
/// <summary>
/// An inner (nested) error.
/// </summary>
[JsonProperty( ReferenceLoopHandling = ReferenceLoopHandling.Ignore, IsReference = true )]
public ExceptionInfo Inner { get; set; }
// ...code omitted for brevity...
}
希望有帮助,Jaans
要忽略循环引用而不在MVC 6中全局对其进行序列化,请在startup.cs中使用以下内容:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(options =>
{
options.OutputFormatters.RemoveTypesOf<JsonOutputFormatter>();
var jsonOutputFormatter = new JsonOutputFormatter();
jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
options.OutputFormatters.Insert(0, jsonOutputFormatter);
});
}
在WebApiConfig.cs
课堂上使用这个:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
对我来说,我不得不走另一条路。不必尝试修复JSON.Net序列化程序,我必须在datacontext上进行延迟加载之后。
我刚刚将其添加到我的基本存储库中:
context.Configuration.ProxyCreationEnabled = false;
因为我使用依赖项注入,所以“上下文”对象是我在基础存储库中使用的构造函数参数。您可以在实例化数据上下文的任何位置更改ProxyCreationEnabled属性。
http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html
我有这个例外,并且我的工作解决方案很简单,
通过向其添加JsonIgnore属性来忽略Referenced属性:
[JsonIgnore]
public MyClass currentClass { get; set; }
反序列化时重置属性:
Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
{
Source.MyClass = item;
}
使用Newtonsoft.Json;
[JsonIgnore]
人们已经谈论过[JsonIgnore]被添加到类的虚拟属性中,例如:
[JsonIgnore]
public virtual Project Project { get; set; }
我还将共享另一个选项[JsonProperty(NullValueHandling = NullValueHandling.Ignore)],该选项仅在属性为null时才将其从序列化中删除:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
对于.NET Core 3.0,更新Startup.cs类,如下所示。
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers()
.AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
请参阅:https : //devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-0-preview-5/
使用自定义配置JsonSerializerSettings解决了我的问题:
services.AddMvc(
// ...
).AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Serialize;
opt.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
});
我遇到了同样的问题,我尝试使用JsonSetting忽略自引用错误,这有点儿麻烦,直到我得到了一个非常自引用的类,并且我的dot-net进程挂在Json编写值上。
我的问题
public partial class Company : BaseModel
{
public Company()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string Name { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
public partial class CompanyUser
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int UserId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class User : BaseModel
{
public User()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string DisplayName { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
您可以在引用CompanyUser的 User类中看到问题类,它是一个自引用。
现在,我正在调用包含所有关系属性的GetAll方法。
cs.GetAll("CompanyUsers", "CompanyUsers.User");
在此阶段,我的DotNetCore进程挂在执行JsonResult上,写入值...并永不结束。在我的Startup.cs中,我已经设置了JsonOption。由于某些原因,EFCore包含了我不要求Ef提供的嵌套属性。
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
预期的行为应该是这样
嗨,EfCore您能否在我的Company类中也包含“ CompanyUsers”数据,以便我可以轻松访问这些数据。
然后
嗨,EfCore还可以同时包含“ CompanyUsers.User”数据,这样我就可以轻松访问像Company.CompanyUsers.First()。User.DisplayName这样的数据了 吗?
在此阶段,我应该只获得此“ Company.CompanyUsers.First()。User.DisplayName”,并且不应给我造成自我引用问题的Company.CompanyUsers.First()。User.CompanyUsers;从技术上讲,它不应该给我User.CompanyUsers,因为CompanyUsers是导航属性。但是,EfCore感到非常兴奋,并给了我User.CompanyUsers。
因此,我决定编写一种扩展方法,以将属性从对象中排除(实际上不排除它只是将属性设置为null)。不仅如此,它还将对数组属性起作用。下面是我还将为其他用户导出nuget包的代码(不确定是否可以帮助某人)。原因很简单,因为我懒得写.Select(n => new {n.p1,n.p2}); 我只是不想编写select语句以仅排除1个属性!
这不是最好的代码(我会在某个阶段进行更新),因为我急忙编写,尽管这可能也有助于希望使用数组排除(设置null)对象的人。
public static class PropertyExtensions
{
public static void Exclude<T>(this T obj, Expression<Func<T, object>> expression)
{
var visitor = new PropertyVisitor<T>();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
List<MemberInfo> paths = visitor.Path;
Action<List<MemberInfo>, object> act = null;
int recursiveLevel = 0;
act = (List<MemberInfo> vPath, object vObj) =>
{
// set last propert to null thats what we want to avoid the self-referencing error.
if (recursiveLevel == vPath.Count - 1)
{
if (vObj == null) throw new ArgumentNullException("Object cannot be null");
vObj.GetType().GetMethod($"set_{vPath.ElementAt(recursiveLevel).Name}").Invoke(vObj, new object[] { null });
return;
}
var pi = vObj.GetType().GetProperty(vPath.ElementAt(recursiveLevel).Name);
if (pi == null) return;
var pv = pi.GetValue(vObj, null);
if (pi.PropertyType.IsArray || pi.PropertyType.Name.Contains("HashSet`1") || pi.PropertyType.Name.Contains("ICollection`1"))
{
var ele = (IEnumerator)pv.GetType().GetMethod("GetEnumerator").Invoke(pv, null);
while (ele.MoveNext())
{
recursiveLevel++;
var arrItem = ele.Current;
act(vPath, arrItem);
recursiveLevel--;
}
if (recursiveLevel != 0) recursiveLevel--;
return;
}
else
{
recursiveLevel++;
act(vPath, pv);
}
if (recursiveLevel != 0) recursiveLevel--;
};
// check if the root level propert is array
if (obj.GetType().IsArray)
{
var ele = (IEnumerator)obj.GetType().GetMethod("GetEnumerator").Invoke(obj, null);
while (ele.MoveNext())
{
recursiveLevel = 0;
var arrItem = ele.Current;
act(paths, arrItem);
}
}
else
{
recursiveLevel = 0;
act(paths, obj);
}
}
public static T Explode<T>(this T[] obj)
{
return obj.FirstOrDefault();
}
public static T Explode<T>(this ICollection<T> obj)
{
return obj.FirstOrDefault();
}
}
上面的扩展类将使您能够将属性设置为null以避免自引用循环甚至数组。
表情生成器
internal class PropertyVisitor<T> : ExpressionVisitor
{
public readonly List<MemberInfo> Path = new List<MemberInfo>();
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
Path.Add(node.Member);
return base.VisitMember(node);
}
}
用途:
模型类
public class Person
{
public string Name { get; set; }
public Address AddressDetail { get; set; }
}
public class Address
{
public string Street { get; set; }
public Country CountryDetail { get; set; }
public Country[] CountryDetail2 { get; set; }
}
public class Country
{
public string CountryName { get; set; }
public Person[] CountryDetail { get; set; }
}
虚拟数据
var p = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail = new Country
{
CountryName = "AU"
}
}
};
var p1 = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail2 = new Country[]
{
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
}
}
};
情况:
情况1:仅排除没有任何数组的属性
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
情况2:排除具有1个数组的属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
情况3:排除具有2个嵌套数组的属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
情况4:包含的EF GetAll查询
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
您已经注意到Explode()方法也是它的扩展方法,仅用于表达式构建器从数组属性获取属性。每当有数组属性时,请使用.Explode()。YourPropertyToExclude或.Explode()。Property1.MyArrayProperty.Explode()。MyStupidProperty。上面的代码帮助我避免了自我引用的深度。现在,我可以使用GetAll并排除我不想要的属性!
感谢您阅读这篇重要文章!
对于不循环这对我有用-
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
我已经在这里解决了所有问题-使用.Net Core 2 WebAPI进行实体框架子级序列化 https://gist.github.com/Kaidanov/f9ad0d79238494432f32b8407942c606
将不胜感激。也许有人可以在某个时候使用它。
C#代码:
var jsonSerializerSettings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var jsonString = JsonConvert.SerializeObject(object2Serialize, jsonSerializerSettings);
var filePath = @"E:\json.json";
File.WriteAllText(filePath, jsonString);