C# 检测到实体框架自引用循环
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19467673/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entity framework self referencing loop detected
提问by Lydon
I have a strange error. I'm experimenting with a .NET 4.5 Web API, Entity Framework and MS SQL Server. I've already created the database and set up the correct primary and foreign keys and relationships.
我有一个奇怪的错误。我正在试验 .NET 4.5 Web API、实体框架和 MS SQL Server。我已经创建了数据库并设置了正确的主键和外键以及关系。
I've created a .edmx model and imported two tables: Employee and Department. A department can have many employees and this relationship exists. I created a new controller called EmployeeController using the scaffolding options to create an API controller with read/write actions using Entity Framework. In the wizard, selected Employee as the model and the correct entity for the data context.
我创建了一个 .edmx 模型并导入了两个表:员工和部门。一个部门可以有很多员工,这种关系是存在的。我使用脚手架选项创建了一个名为 EmployeeController 的新控制器,以使用实体框架创建具有读/写操作的 API 控制器。在向导中,选择 Employee 作为模型和数据上下文的正确实体。
The method that is created looks like this:
创建的方法如下所示:
public IEnumerable<Employee> GetEmployees()
{
var employees = db.Employees.Include(e => e.Department);
return employees.AsEnumerable();
}
When I call my API via /api/Employee, I get this error:
当我通过 /api/Employee 调用我的 API 时,出现此错误:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; ...System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Self referencing loop detected with type 'System.Data.Entity.DynamicProxies.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552'. Path '[0].Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":" ...
“ObjectContent`1”类型无法序列化内容类型“application/json”的响应主体;...System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"发生错误。","ExceptionMessage":"检测到类型为 'System.Data.Entity.DynamicProxies 的自引用循环.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552'。路径 '[0].Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":" ...
Why is it self referencing [0].Department.Employees? That doesn't make a whole lot of sense. I would expect this to happen if I had circular referencing in my database but this is a very simple example. What could be going wrong?
为什么是自引用 [0].Department.Employees?这没有多大意义。如果我的数据库中有循环引用,我希望会发生这种情况,但这是一个非常简单的示例。可能出什么问题了?
采纳答案by Pedro Figueiredo
Well the correct answer for the default Json formater based on Json.net is to set ReferenceLoopHandling
to Ignore
.
那么基于 Json.net 的默认 Json 格式化程序的正确答案是设置ReferenceLoopHandling
为Ignore
.
Just add this to the Application_Start
in Global.asax:
只需将其添加到Application_Start
Global.asax 中:
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
This is the correct way. It will ignore the reference pointing back to the object.
这是正确的方法。它将忽略指向对象的引用。
Other responses focused in changing the list being returned by excluding data or by making a facade object and sometimes that is not an option.
其他响应集中在通过排除数据或通过创建外观对象来更改返回的列表,有时这不是一个选项。
Using the JsonIgnore
attribute to restrict the references can be time consuming and if you want to serialize the tree starting from another point that will be a problem.
使用JsonIgnore
属性来限制引用可能很耗时,如果您想从另一个点开始序列化树,这将是一个问题。
回答by Nicolás Straub
This happens because you're trying to serialize the EF object collection directly. Since department has an association to employee and employee to department, the JSON serializer will loop infinetly reading d.Employee.Departments.Employee.Departments etc...
发生这种情况是因为您试图直接序列化 EF 对象集合。由于部门与员工和员工与部门有关联,因此 JSON 序列化程序将无限循环读取 d.Employee.Departments.Employee.Departments 等...
To fix this right before the serialization create an anonymous type with the props you want
要在序列化之前解决此问题,请使用您想要的道具创建匿名类型
example (psuedo)code:
示例(伪)代码:
departments.select(dep => new {
dep.Id,
Employee = new {
dep.Employee.Id, dep.Employee.Name
}
});
回答by Nitin Dominic
The main problem is that serializing an entity model which has relation with other entity model(Foreign key relationship). This relation causes self referencing this will throw exception while serialization to json or xml. There are lots of options. Without serializing entity models by using custom models.Values or data from entity model data mapped to custom models(object mapping) using Automapperor Valueinjectorthen return request and it will serialize without any other issues. Or you can serialize entity model so first disable proxies in entity model
主要问题是序列化与其他实体模型(外键关系)有关系的实体模型。这种关系导致自引用这将在序列化为 json 或 xml 时抛出异常。有很多选择。如果不通过使用自定义models.Values或数据从映射到定制机型(对象映射)采用实体模型数据序列化实体模型Automapper或Valueinjector然后返回请求,它会序列化,没有任何其他问题。或者您可以序列化实体模型,因此首先禁用实体模型中的代理
public class LabEntities : DbContext
{
public LabEntities()
{
Configuration.ProxyCreationEnabled = false;
}
To preserve object references in XML, you have two options. The simpler option is to add [DataContract(IsReference=true)] to your model class. The IsReference parameter enables oibject references. Remember that DataContract makes serialization opt-in, so you will also need to add DataMember attributes to the properties:
要保留 XML 中的对象引用,您有两种选择。更简单的选择是将 [DataContract(IsReference=true)] 添加到您的模型类。IsReference 参数启用对象引用。请记住,DataContract 选择加入序列化,因此您还需要向属性添加 DataMember 属性:
[DataContract(IsReference=true)]
public partial class Employee
{
[DataMember]
string dfsd{get;set;}
[DataMember]
string dfsd{get;set;}
//exclude the relation without giving datamember tag
List<Department> Departments{get;set;}
}
In Json format in global.asax
global.asax 中的 Json 格式
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
in xml format
xml格式
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue,
false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Employee>(dcs);
回答by B-Lat
I had same problem and found that you can just apply the [JsonIgnore]
attribute to the navigation property you don't want to be serialised. It will still serialise both the parent and child entities but just avoids the self referencing loop.
我遇到了同样的问题,发现您可以将该[JsonIgnore]
属性应用于不想序列化的导航属性。它仍然会序列化父实体和子实体,但只是避免了自引用循环。
回答by sobelito
I might also look into adding explicit samples for each controller/action, as well covered here:
我也可能会考虑为每个控制器/动作添加显式示例,这里也有介绍:
i.e. config.SetActualResponseType(typeof(SomeType), "Values", "Get");
即 config.SetActualResponseType(typeof(SomeType), "Values", "Get");
回答by Thomas B. Lze
The message error means that you have a self referencing loop.
消息错误意味着您有一个自引用循环。
The json you produce is like this example (with a list of one employee) :
您生成的 json 类似于此示例(包含一名员工的列表):
[
employee1 : {
name: "name",
department : {
name: "departmentName",
employees : [
employee1 : {
name: "name",
department : {
name: "departmentName",
employees : [
employee1 : {
name: "name",
department : {
and again and again....
}
]
}
}
]
}
}
]
]
You have to tell the db context that you don't want to get all linked entities when you request something.
The option for DbContext is Configuration.LazyLoadingEnabled
您必须告诉 db 上下文,当您请求某些内容时,您不想获取所有链接的实体。DbContext 的选项是Configuration.LazyLoadingEnabled
The best way I found is to create a context for serialization :
我发现的最好方法是为序列化创建上下文:
public class SerializerContext : LabEntities
{
public SerializerContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
回答by newstockie
Add a line Configuration.ProxyCreationEnabled = false;
in constructor of your context model partial class definition.
Configuration.ProxyCreationEnabled = false;
在上下文模型部分类定义的构造函数中添加一行。
public partial class YourDbContextModelName : DbContext
{
public YourDbContextModelName()
: base("name=YourDbContextConn_StringName")
{
Configuration.ProxyCreationEnabled = false;//this is line to be added
}
public virtual DbSet<Employee> Employees{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
回答by sindrem
I only had one model i wanted to use, so i ended up with the following code:
我只有一个我想使用的模型,所以我最终得到了以下代码:
var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
回答by Piotrek
I'm aware that question is quite old, but it's still popular and I can't see any solution for ASP.net Core.
我知道这个问题已经很老了,但它仍然很流行,我看不到 ASP.net Core 的任何解决方案。
I case of ASP.net Core, you need to add new JsonOutputFormatter
in Startup.cs
file:
我是ASP.net Core 的情况,你需要JsonOutputFormatter
在Startup.cs
文件中添加新的:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}, ArrayPool<char>.Shared));
});
//...
}
After implementing it, JSON serializer will simply ignore loop references. What it means is: it will return null instead of infinitely loading objects referencing each other.
实现之后,JSON 序列化器将简单地忽略循环引用。它的意思是:它将返回 null 而不是无限加载相互引用的对象。
Without above solution using:
没有上述解决方案使用:
var employees = db.Employees.ToList();
Would load Employees
and related to them Departments
.
会加载Employees
并与它们相关Departments
。
After setting ReferenceLoopHandling
to Ignore
, Departments
will be set to null unless you include it in your query:
设置ReferenceLoopHandling
为 后Ignore
,Departments
除非您将其包含在查询中,否则将设置为 null:
var employees = db.Employees.Include(e => e.Department);
Also, keep in mind that it will clear all OutputFormatters, if you don't want that you can try removing this line:
另外,请记住,它会清除所有OutputFormatters,如果您不想要,可以尝试删除此行:
options.OutputFormatters.Clear();
But removing it causes again self referencing loop
exception in my case for some reason.
但是self referencing loop
由于某种原因,在我的情况下删除它会再次导致异常。
回答by counter arguments
Self-referencing as example
以自引用为例
public class Employee {
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int ManagerId { get; set; }
public virtual Employee Manager { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
public Employee() {
Employees = new HashSet<Employee>();
}
}
HasMany(e => e.Employees)
.WithRequired(e => e.Manager)
.HasForeignKey(e => e.ManagerId)
.WillCascadeOnDelete(false);