检测到实体框架自引用循环

Lyd*_*don 100 c# serialization entity-framework-4 asp.net-web-api

我有一个奇怪的错误.我正在试验.NET 4.5 Web API,实体框架和MS SQL Server.我已经创建了数据库并设置了正确的主键和外键以及关系.

我创建了一个.edmx模型并导入了两个表:Employee和Department.一个部门可以有很多员工,这种关系存在.我使用scaffolding选项创建了一个名为EmployeeController的新控制器,以使用Entity Framework创建一个带有读/写操作的API控制器.在向导中,选择Employee作为模型,并选择数据上下文的正确实体.

创建的方法如下所示:

public IEnumerable<Employee> GetEmployees()
{
    var employees = db.Employees.Include(e => e.Department);
    return employees.AsEnumerable();
}
Run Code Online (Sandbox Code Playgroud)

当我通过/ api/Employee调用我的API时,出现此错误:

'ObjectContent`1'类型无法序列化内容类型'application/json的响应主体; ... ... System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"发生错误.","ExceptionMessage":"使用类型'System.Data.Entity.DynamicProxies检测到自引用循环.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552' .Path'[0] .Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":"...

为什么它自引用[0] .Department.Employees?这并没有多大意义.如果我在我的数据库中进行循环引用,我希望这会发生,但这是一个非常简单的例子.怎么可能出错?

小智 155

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

只需将其添加到Application_StartGlobal.asax中:

HttpConfiguration config = GlobalConfiguration.Configuration;

config.Formatters.JsonFormatter
            .SerializerSettings
            .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Run Code Online (Sandbox Code Playgroud)

这是正确的方法.它将忽略指向对象的引用.

其他响应的重点是通过排除数据或通过制作Facade对象来更改返回的列表,有时这不是一个选项.

使用该JsonIgnore属性来限制引用可能非常耗时,并且如果要从另一个点开始序列化树将是一个问题.

  • 没有效果,异常仍然发生 (12认同)
  • 即使我在“Application_Start”中有上面的代码并且在相关属性上有“[JsonIgnore]”,我也会收到自引用循环错误……我在这些属性上也有“[IgnoreDataMember]”,并设置了“ProxyCreationEnabled”实体框架的`到`false`... (2认同)

Nic*_*aub 52

发生这种情况是因为您正在尝试直接序列化EF对象集合.由于部门与员工和员工之间存在关联,因此JSON序列化程序将循环读取d.Employee.Departments.Employee.Departments等...

要在序列化之前修复此问题,请创建一个包含所需道具的匿名类型

示例(伪)代码:

departments.select(dep => new { 
    dep.Id, 
    Employee = new { 
        dep.Employee.Id, dep.Employee.Name 
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 这是正确的答案.你需要告诉EF你需要什么. (2认同)

B-L*_*Lat 32

我遇到了同样的问题,发现你可以将[JsonIgnore]属性应用到你不希望被序列化的导航属性.它仍将序列化父实体和子实体,但只是避免了自引用循环.


Pio*_*rek 18

我知道这个问题很老了,但它仍然很受欢迎,我看不到ASP.net Core的任何解决方案.

我是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));
        });

        //...
    }
Run Code Online (Sandbox Code Playgroud)

实现它之后,JSON序列化器将简单地忽略循环引用.它的含义是:它将返回null而不是无限地加载彼此引用的对象.

没有以上解决方案使用

var employees = db.Employees.ToList();
Run Code Online (Sandbox Code Playgroud)

会加载Employees并与它们相关Departments.

设置ReferenceLoopHandling为之后Ignore,Departments除非将其包含在查询中,否则将设置为null:

var employees = db.Employees.Include(e => e.Department);
Run Code Online (Sandbox Code Playgroud)

另外,请记住它将清除所有OutputFormatters,如果您不希望您可以尝试删除此行:

options.OutputFormatters.Clear();
Run Code Online (Sandbox Code Playgroud)

self referencing loop由于某些原因,删除它会导致我的情况再次出现异常.


Nit*_*nic 8

主要问题是序列化与其他实体模型(外键关系)有关系的实体模型.这种关系导致自引用,这将在序列化为json或xml时抛出异常.有很多选择.如果不通过使用自定义models.Values或数据从映射到定制机型(对象映射)采用实体模型数据序列化实体模型AutomapperValueinjector然后返回请求,它会序列化,没有任何其他问题.或者您可以序列化实体模型,因此首先在实体模型中禁用代理

public class LabEntities : DbContext
{
   public LabEntities()
   {
      Configuration.ProxyCreationEnabled = false;
   }
Run Code Online (Sandbox Code Playgroud)

要保留XML中的对象引用,您有两个选择.更简单的选项是将[DataContract(IsReference = true)]添加到模型类中.IsReference参数启用oibject引用.请记住,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;}
}
Run Code Online (Sandbox Code Playgroud)

在global.asax中以Json格式

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.All;
Run Code Online (Sandbox Code Playgroud)

以xml格式

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Employee>(dcs);
Run Code Online (Sandbox Code Playgroud)


小智 8

消息错误意味着您有一个自引用循环.

你生成的json就像这个例子(有一个雇员的列表):

[
employee1 : {
    name: "name",
    department : {
        name: "departmentName",
        employees : [
            employee1 : {
                name: "name",
                department : {
                    name: "departmentName",
                    employees : [
                        employee1 : {
                            name: "name",
                            department : {
                                and again and again....
                            }
                    ]
                }
            }
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

]

您必须告诉db上下文,当您请求某些内容时,您不希望获得所有链接的实体.DbContext的选项是Configuration.LazyLoadingEnabled

我找到的最好方法是创建序列化的上下文:

public class SerializerContext : LabEntities 
{
    public SerializerContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 7

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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)


sin*_*rem 6

我只有一个我想使用的模型,所以我最终得到了以下代码:

var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
Run Code Online (Sandbox Code Playgroud)


小智 6

我刚刚在我的 .net core 网站上遇到了同样的问题。接受的答案对我不起作用,但我发现 ReferenceLoopHandling.Ignore 和 PreserveReferencesHandling.Objects 的组合修复了它。

//serialize item
var serializedItem = JsonConvert.SerializeObject(data, Formatting.Indented, 
new JsonSerializerSettings
{
     PreserveReferencesHandling = PreserveReferencesHandling.Objects,
     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Run Code Online (Sandbox Code Playgroud)


Ale*_*ath 5

如果你想在Blazor(ASP.NET核心托管)模板来更改此设置,您需要将以下传递到AddNewtonsoftJson通话中Startup.csServer项目:

services.AddMvc().AddNewtonsoftJson(options => 
    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
);
Run Code Online (Sandbox Code Playgroud)