. Net Core 3.0可能的对象周期被检测到,这是不支持的

我有两个实体,它们作为一个对多个实体相关联

public class Restaurant {
public int RestaurantId {get;set;}
public string Name {get;set;}
public List<Reservation> Reservations {get;set;}
...
}
public class Reservation{
public int ReservationId {get;set;}
public int RestaurantId {get;set;}
public Restaurant Restaurant {get;set;}
}

如果我想用我的 API 订餐

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


我收到响应错误,因为对象包含对彼此的引用。 有相关的职位,推荐 创建独立的模型 或者加入 Newton softJson 配置

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

System.Text.JsonJsonException: 检测到可能的对象循环 这可能是由于循环或者 物体深度大于最大允许深度32.at ThrowHelper.ThrowInvalidOperationException _ SerializerCycleDetected (Int32 在 System.Text.Json.JsonSerializer. Write (Utf8JsonWriter 作者,Int32原创作者深度, JsonSerializerOptions 选项、 WriteStack & state) 系统。文本。 Json.JsonSerializer。 WriteAsyncCore (Stream utf8Json,Object 值,键入 inputType,JsonSerializerOptions 选项, 取消令牌取消令牌) 格式化。 SystemTextJsonOutputFormatter。 WriteResponseBodyAsync (OutputFormatterWriteContext 上下文,编码选择编码) 格式化。 SystemTextJsonOutputFormatter。 WriteResponseBodyAsync (OutputFormatterWriteContext 上下文,编码选择编码) 基础设施.ResourceInvoker.g _ _ Await | 29 _ 0[ TFilter,TFilterAsync ](ResourceInvoker 调用程序,任务 lastTask,状态下一步,范围作用域,对象状态,布尔值 完成) 基础设施。资源调用器。重新抛出(结果执行 上下文) 结果下一步[ TFilter,TFilterAsync ](状态 & 下一步,作用域和作用域,对象和状态,布尔值和完成)在 基础设施。资源调用器。调用结果过滤器()

*

212546 次浏览

在启动时设置 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;}
}

我已经在一个新的项目中尝试了你的代码,第二种方法在第一次为3.0安装了包 微软。 AspNetCore之后似乎工作得很好

services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

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

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

Cs Add service

services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
public class Reservation{
public int ReservationId {get;set;}
public int RestaurantId {get;set;}
[JsonIgnore]
public Restaurant Restaurant {get;set;}

以上方法也有效,但我更喜欢以下方法

services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

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

谁仍然面临这个问题: 检查是否对所有的异步方法进行了 await编码。

这使用 System.Text.Json 就可以工作

var options = new JsonSerializerOptions()
{
MaxDepth = 0,
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true
};

使用选项序列化

objstr = JsonSerializer.Serialize(obj,options);

我碰到这个问题,我感到困惑,因为我有另一个应用程序运行相同的代码,唯一的区别是我想使用等待这一次,并在最后一个应用程序我使用 ConfigureAwait(false).GetAwaiter().GetResult();

因此,通过删除 wait 并在 Async 方法的末尾添加 ConfigureAwait(false).GetAwaiter().GetResult(),我能够解决这个问题。

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

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

当我在控制器方法中错误地返回 Task<object>而不是 object时,我得到了这样一个错误。这个任务导致了一个循环。检查你要退还的东西。

我遇到了这种情况,我不得不通过在 db 上下文类的 OnModelCreate (ModelBuilder 构建器)方法中添加以下内容来告诉 app/context 忽略子实体上的父实体:

builder.Entity<ChildEntity>()
.HasOne(a => a.ParentEntity)
.WithMany(m => m.ChildEntities);
builder.Entity<ChildEntity>().Ignore(a => a.ParentEntity);

无视的最后一句台词就是它为我做到的。

对于那些没有找到其他解决方案的人,您实际上需要分析完整的调用堆栈,并查看是否有任何 async调用没有在预期等待它的地方 awaited。这就是问题中提到的实际问题。

例如,考虑 MyAppService中的以下方法,它调用 MyOtherServiceasync Task<int>:

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

如果 CreateAndGetIdAsync方法是 async Task,那么对上面的 Create 方法的调用将通过上面提到的给定异常进行。这是因为序列化会中断,因为 idTask<int>,而实际上不是 int。所以在返回响应之前必须先进行 await

附加说明: 这里需要注意的是,即使出现这种异常,也不会影响 db 操作。例如,在上面的示例中,db 操作将是成功的。类似地,正如在 OP 中提到的,异常并没有被抛出我的 ORM,但是这个异常是随后按照调用堆栈的顺序(在一个调用者中)抛出的。

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

这个错误是因为:

ASP.NET Core 3.0和上述版本中使用的默认 JSON 序列化器。

NET Core 3.0移除了对 JSON.NET 的依赖,并使用了自己的 JSON 序列化器,即 System. Text. JSON’。

我能够解决这个问题,添加参考 NewtonsoftJson Nuget 软件包,

PM> Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.2

更新 Startup.cs 如下所示,

services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

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

接受的答案将默认序列化程序从 System 更改。短信。Json 到 Newtonsoft,并将通过从序列化中删除导航属性来解决这个循环!

订购例子:

{
"id": "d310b004-79a2-4661-2f90-08d8d25fec03"
"orderItems": [
{
"orderId": "d310b004-79a2-4661-2f90-08d8d25fec03",
"orderItemId": "83d36eda-ba03-448c-e53c-08d8d25fec0b",
"orderItem": {
"id": "83d36eda-ba03-448c-e53c-08d8d25fec0b",
"name": "My order item"
}
// where is the reference to the order?!
}
]
}

如果不想更改默认序列化程序,或者需要保留导航属性,可以配置 System。短信。Json 序列化程序来保存引用。但是要小心,因为它通过提供 $id、 $ref 和 $价值属性改变了输出结构!

services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve)

订购例子:

{
"$id": "1",
"id": "d310b004-79a2-4661-2f90-08d8d25fec03"
"orderItems": {
$"id": "2",
$"values": [
{
$"id": "3",
"orderId": "d310b004-79a2-4661-2f90-08d8d25fec03",
"orderItemId": "83d36eda-ba03-448c-e53c-08d8d25fec0b",
"orderItem": {
"id": "83d36eda-ba03-448c-e53c-08d8d25fec0b",
"name": "My order item"
},
"order": {
"$ref": "1" // reference to the order
}
}
]
}
}

更新:

使用 .NET 6System.Text.Json可以选择忽略这样的循环引用:

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

Https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references?pivots=dotnet-6-0#ignore-circular-references

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

来源:

Https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references?pivots=dotnet-6-0#ignore-circular-references

例如 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"
//   }
// }

Https://www.newtonsoft.com/json/help/html/referenceloophandlingignore.htm

原文:

我在使用 API Controller with actions, using entity framework创建的 Controller 中的默认 POST 方法中得到了这个错误。

return CreatedAtAction("GetLearningObjective", new { id = learningObjective.Id }, learningObjective);

System.Text.JsonJsonException: 检测到可能的对象循环。 这可能是由于一个周期或如果对象深度较大 超过最大允许深度32。考虑使用 保留在 JsonSerializerOptions 上以支持循环。 在 已检测到 System.Text.Json.ThrowHelper.ThrowJsonException _ SerializerCycleDetected (Int32 最大深度)

当从邮递员或浏览器直接调用 HttpGet时,它工作正常。通过编辑 Startup.cs-services.AddControllers()解决了这个问题:

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

你也可以这样解:

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

Https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references?pivots=dotnet-5-0

正如@Jozkee 在公认答案的评论中所说,.NET 6在 System.Text.Json中包含 ReferenceHandler.IgnoreCycles

这就是我在没有安装牛顿软件的情况下解决这个问题的方法。Json 和使用新添加到。NET 6,方法是将以下内容添加到 Program.cs


builder.Services.AddControllersWithViews()
.AddJsonOptions(options => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);


这里有一个 关于对象循环的有益文章的任何人谁不知道他们是什么。

我没有使用 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;
});
}

6号网

在程序设计中

builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
options.JsonSerializerOptions.WriteIndented = true;
});

我知道这个问题是为了。但是对于任何遇到同样问题的人。净5.0,我发现了一个解决方案 给你

总之,需要在 Startup 类的 ConfigureServices 方法中添加以下代码:

services.AddControllers().AddJsonOptions(x =>
x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);

最小 API 使用:

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

对于.Net 核心6,将其添加到 程序解决了我的问题

builder.Services.AddControllers().AddJsonOptions(x =>
x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);