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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 15:04:45  来源:igfitidea点击:

Entity framework self referencing loop detected

c#serializationentity-framework-4asp.net-web-api

提问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 ReferenceLoopHandlingto Ignore.

那么基于 Json.net 的默认 Json 格式化程序的正确答案是设置ReferenceLoopHandlingIgnore.

Just add this to the Application_Startin Global.asax:

只需将其添加到Application_StartGlobal.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 JsonIgnoreattribute 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或数据从映射到定制机型(对象映射)采用实体模型数据序列化实体模型AutomapperValueinjector然后返回请求,它会序列化,没有任何其他问题。或者您可以序列化实体模型,因此首先禁用实体模型中的代理

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:

我也可能会考虑为每个控制器/动作添加显式示例,这里也有介绍:

http://blogs.msdn.com/b/yaohuang1/archive/2012/10/13/asp-net-web-api-help-page-part-2-providing-custom-samples-on-the-help-page.aspx

http://blogs.msdn.com/b/yaohuang1/archive/2012/10/13/asp-net-web-api-help-page-part-2-providing-custom-samples-on-the-help-页面.aspx

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 JsonOutputFormatterin Startup.csfile:

我是ASP.net Core 的情况,你需要JsonOutputFormatterStartup.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 Employeesand related to them Departments.

会加载Employees并与它们相关Departments

After setting ReferenceLoopHandlingto Ignore, Departmentswill be set to null unless you include it in your query:

设置ReferenceLoopHandling为 后IgnoreDepartments除非您将其包含在查询中,否则将设置为 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 loopexception 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);