检测到不支持的 .Net Core 3.0 可能的对象循环

Naz*_*lyp 119 c# entity-framework-core asp.net-core

我有 2 个实体,它们是一对多相关的

public class Restaurant {
   public int RestaurantId {get;set;}
   public string Name {get;set;}
   public List<Reservation> Reservations {get;set;}
   ...
}
Run Code Online (Sandbox Code Playgroud)
public class Reservation{
   public int ReservationId {get;set;}
   public int RestaurantId {get;set;}
   public Restaurant Restaurant {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试使用我的 api 预订餐厅

   var restaurants =  await _dbContext.Restaurants
                .AsNoTracking()
                .AsQueryable()
                .Include(m => m.Reservations).ToListAsync();
    .....

Run Code Online (Sandbox Code Playgroud)

我收到错误响应,因为对象包含彼此的引用。有相关帖子建议创建单独的模型 或添加NewtonsoftJson 配置

问题是我不想创建单独的模型,第二个建议没有帮助。有没有办法在没有循环关系的情况下加载数据?*

System.Text.Json.JsonException: 检测到不支持的可能的对象循环。这可能是由于循环或对象深度大于最大允许深度 32。 at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(Int32 maxDepth) at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancelationToken) at Microsoft.AspNetCore.Mvc.OutputFormatterSystem.TextJFormatterSystem. Microsoft.AspNetCore.Mvc 上的 WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)。

*

Rya*_*yan 180

我已经在一个新项目中尝试过你的代码,在首先为 3.0安装软件包Microsoft.AspNetCore.Mvc.NewtonsoftJson后,第二种方法似乎工作得很好

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

尝试一个新项目并比较差异。

  • 随着系统json性能的提高,我们必须使用NewtonsoftJson,这不是错误的吗?:/ (12认同)
  • 从 .NET 5 System.Text.Json 开始包含选项 [`ReferenceHandler.Preserve`](https://learn.microsoft.com/dotnet/api/system.text.json.serialization.referencehandler.preserve?view=net -6.0) 和 .NET 6 包含 [`ReferenceHandler.IgnoreCycles`](https://learn.microsoft.com/dotnet/api/system.text.json.serialization.referencehandler.ignorecycles?view=net-6.0); 两者都是非常好的替代方案,可以避免切换回 Newtonsoft.Json 并保持 System.Text.Json 的性能提升。 (7认同)
  • 这里的关键时刻是重新安装正确版本的 Microsoft.AspNetCore.Mvc.NewtonsoftJson 我没有注意版本,因为这个包在盒子下可用,没有任何错误和警告!感谢您的回答!一切都按照我的预期进行! (3认同)
  • @MarekUrbanowicz 调用无法处理循环或字典类型生产就绪的序列化程序是错误的。 (3认同)

小智 145

.NET Core 3.1 安装包 Microsoft.AspNetCore.Mvc.NewtonsoftJson(来自https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson/

Startup.cs 添加服务

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

  • 有关更多详细信息,请检查:https://thecodebuzz.com/jsonexception-possible-object-cycle-Detected-object-depth/ (3认同)

Ogg*_*las 64

更新:

使用 .NET 6,有一个选项可以System.Text.Json忽略循环引用,如下所示:

JsonSerializerOptions options = new()
{
    ReferenceHandler = ReferenceHandler.IgnoreCycles,
    WriteIndented = true
};
Run Code Online (Sandbox Code Playgroud)

忽略循环引用

问题ReferenceHandler.Preserve是 JSON 键以 为前缀$,这可能会导致一些问题。

例子System.Text.Json ReferenceHandler.IgnoreCycles

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SerializeIgnoreCycles
{
    public class Employee
    {
        public string Name { get; set; }
        public Employee Manager { get; set; }
        public List<Employee> DirectReports { get; set; }
    }

    public class Program
    {
        public static void Main()
        {
            Employee tyler = new()
            {
                Name = "Tyler Stein"
            };

            Employee adrian = new()
            {
                Name = "Adrian King"
            };

            tyler.DirectReports = new List<Employee> { adrian };
            adrian.Manager = tyler;

            JsonSerializerOptions options = new()
            {
                ReferenceHandler = ReferenceHandler.IgnoreCycles,
                WriteIndented = true
            };

            string tylerJson = JsonSerializer.Serialize(tyler, options);
            Console.WriteLine($"Tyler serialized:\n{tylerJson}");

            Employee tylerDeserialized =
                JsonSerializer.Deserialize<Employee>(tylerJson, options);

            Console.WriteLine(
                "Tyler is manager of Tyler's first direct report: ");
            Console.WriteLine(
                tylerDeserialized.DirectReports[0].Manager == tylerDeserialized);
        }
    }
}

// Produces output like the following example:
//
//Tyler serialized:
//{
//  "Name": "Tyler Stein",
//  "Manager": null,
//  "DirectReports": [
//    {
//      "Name": "Adrian King",
//      "Manager": null,
//      "DirectReports": null
//    }
//  ]
//}
//Tyler is manager of Tyler's first direct report:
//False
Run Code Online (Sandbox Code Playgroud)

来源:

忽略循环引用

示例为Newtonsoft.Json.ReferenceLoopHandling.Ignore

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }
}

Employee joe = new Employee { Name = "Joe User" };
Employee mike = new Employee { Name = "Mike Manager" };
joe.Manager = mike;
mike.Manager = mike;

string json = JsonConvert.SerializeObject(joe, Formatting.Indented, new JsonSerializerSettings
{
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

Console.WriteLine(json);
// {
//   "Name": "Joe User",
//   "Manager": {
//     "Name": "Mike Manager"
//   }
// }
Run Code Online (Sandbox Code Playgroud)

参考循环处理设置

原来的:

我从使用ADO.NET Entity Framework使用带有操作的 API 控制器创建的控制器中的默认 POST 方法收到此错误。

return CreatedAtAction("GetLearningObjective", new { id = learningObjective.Id }, learningObjective);
Run Code Online (Sandbox Code Playgroud)

System.Text.Json.JsonException:检测到可能的对象循环。这可能是由于循环造成的,或者对象深度大于允许的最大深度 32。请考虑在 JsonSerializerOptions 上使用 ReferenceHandler.Preserve 来支持循环。在 System.Text.Json.ThrowHelper.ThrowJsonException_SerializerCycleDetected(Int32 maxDepth)

HttpGet当直接从Postman或浏览器调用时,它工作没有问题。通过编辑解决了这个问题Startup.cs-services.AddControllers()像这样:

services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
Run Code Online (Sandbox Code Playgroud)

你也可以这样解决:

services.AddControllers(options =>
{
    options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
    options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonSerializerOptions(JsonSerializerDefaults.Web)
    {
        ReferenceHandler = ReferenceHandler.Preserve,
    }));
});
Run Code Online (Sandbox Code Playgroud)

如何在 System.Text.Json 中保留引用并处理或忽略循环引用

  • @MuhammedYILMAZ 我绝对会说,在许多情况下,将循环引用属性设置为 null 是一种解决方案。我确实添加了一个“System.Text.Json”“ReferenceHandler.IgnoreCycles”与“Newtonsoft.Json.ReferenceLoopHandling.Ignore”的示例来澄清差异。 (2认同)

Mit*_*áti 52

谁仍然面临这个问题:检查你是否await-ed 所有异步方法。

  • 接受的答案可能是有效的,但正确的答案应该是这个。事实上,未等待该任务会生成暴露的错误。 (4认同)

小智 40

我没有使用 NewtonsoftJson,而是使用System.Text.Json.Serialization

对于.NET Core 3.1

在文件Startup.cs中:

 public void ConfigureServices(IServiceCollection services)
 {
    ..........
    .......

    services.AddControllers().AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        options.JsonSerializerOptions.WriteIndented = true;
    });
 }
Run Code Online (Sandbox Code Playgroud)

对于.NET 6

在文件Program.cs中:

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
    options.JsonSerializerOptions.WriteIndented = true;
});
Run Code Online (Sandbox Code Playgroud)


tim*_*mur 15

在启动时设置 JSON 序列化选项可能是首选方式,因为您将来可能会遇到类似的情况。同时,您可以尝试将数据属性添加到您的模型中,这样它就不会被序列化:https : //www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm

public class Reservation{ 
    public int ReservationId {get;set;} 
    public int RestaurantId {get;set;} 
    [JsonIgnore]
    public Restaurant Restaurant {get;set;} 
}
Run Code Online (Sandbox Code Playgroud)


小智 13

正如Jozkee在已接受答案的评论中所述,.NET 6 包含ReferenceHandler.IgnoreCyclesSystem.Text.Json.

这就是我在不安装 Newtonsoft.Json 的情况下解决此问题的方法,并通过将以下内容添加到 .NET 6 来使用 .NET 6 的新功能Program.cs

builder.Services.AddControllersWithViews()
    .AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
Run Code Online (Sandbox Code Playgroud)

对于那些不确定对象循环是什么的人来说,这是一篇关于对象循环的有用文章。


小智 10

这使用 System.Text.Json

var options = new JsonSerializerOptions()
        {
            MaxDepth = 0,
            IgnoreNullValues = true,
            IgnoreReadOnlyProperties = true
        };
Run Code Online (Sandbox Code Playgroud)

使用选项进行序列化

objstr = JsonSerializer.Serialize(obj,options);
Run Code Online (Sandbox Code Playgroud)

  • 这设置了 MaxDepth,但仍然不允许深度大于 0 的对象通过。我们需要的是忽略 (3认同)

Ken*_*sta 8

MinimalAPI 使用这个:

builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
    options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
Run Code Online (Sandbox Code Playgroud)


小智 7

发生这种情况是因为 JSON 序列化时数据模型之间存在双向关系。

您不应该直接返回您的数据模型。将其映射到新的响应模型然后将其返回。

  • 该问题明确指出“我不想创建单独的模型”。 (2认同)

小智 7

当我错误地返回Task<object>而不是object控制器方法中的 an时,我收到了这样的错误。任务导致循环。检查您要返回的内容。


Zee*_*han 7

对于其他未发现其他解决方案有效的人,您实际上需要分析完整的调用堆栈,并查看是否有任何async调用未await在预期等待的位置进行。这是所提到问题的实际问题。

例如,考虑以下方法 in MyAppService,它调用async Task<int>of MyOtherService

public async Task<int> Create(InputModel input)
{
    var id = _myOtherService.CreateAndGetIdAsync(input);
    return Created("someUri", id);
}
Run Code Online (Sandbox Code Playgroud)

如果CreateAndGetIdAsyncmethod 是async Task,则对上面的 Create 方法的调用将通过问题中提到的给定异常。这是因为序列化会按id原样中断,但实际上Task<int>不会。int因此,必须await在返回响应之前。

附加说明: 这里还需要注意一件事,即使出现此异常,它也不会影响数据库操作。即,在我上面的示例中,数据库操作将成功。类似地,正如OP中提到的,我正在使用的ORM并没有抛出异常,但这个异常是稍后在调用堆栈的序列中抛出的(在调用者之一中)。


lak*_*ara 7

对于 .NET Core 6,将其添加到Program.cs解决了我的问题:

builder.Services.AddControllers().AddJsonOptions(x =>
                x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
Run Code Online (Sandbox Code Playgroud)


Fab*_*Lux 7

微软提出的解决方案

由于 EF Core 会自动修复导航属性,因此最终可能会在对象图中出现循环。例如,加载博客及其相关帖子将生成引用帖子集合的博客对象。每篇文章都会有对博客的引用。

https://learn.microsoft.com/en-us/ef/core/querying/lated-data/serialization

Json.NET

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddMvc()
        .AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        );

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

系统.Text.Json

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddControllers()
        .AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        });

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

我的推荐

不要将数据库模型直接暴露给控制器,这可能会在将来带来一些漏洞。您可以创建从模型到 Dto 类或类似内容的映射。这样您就不需要禁用周期检测机制,因为它对于防止问题很有用。


小智 6

经过几个小时的调试,它确实有一个简单的解决方案。我发现这个链接很有帮助。

这个错误是因为:

ASP.NET Core 3.0 及以上版本中使用的默认 JSON 序列化程序。

ASP.NET Core 3.0 删除了对 JSON.NET 的依赖,并使用它自己的 JSON 序列化程序,即“System.Text.Json”。

我能够解决添加对 NewtonsoftJson Nuget 包的引用的问题,

PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.2
Run Code Online (Sandbox Code Playgroud)

并更新 Startup.cs 如下,

PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.2
Run Code Online (Sandbox Code Playgroud)

System.Text.Json 序列化程序当前不支持 ReferenceLoopHandling


Nan*_*pan 5

public class Reservation{ 
public int ReservationId {get;set;} 
public int RestaurantId {get;set;} 
[JsonIgnore]
public Restaurant Restaurant {get;set;} 
Run Code Online (Sandbox Code Playgroud)

以上也有效。但我更喜欢以下

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

因为首先我们需要将属性添加到我们可能有循环引用的所有模型中。