如何解决 System.Text.Json.JsonException:在 Entity Framework 中检测到可能的对象循环?

How to resolve System.Text.Json.JsonException: A possible object cycle was detected in Entity Framework?

我有一个带有 tables/data 的数据库,因此我使用了数据库优先方法,使用以下命令搭建模型脚手架:

dotnet ef dbcontext scaffold "Server=.;Database=MyDb;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models

这已经生成了 DbContext 和几个模型 类 对应于我的表。这个问题只涉及2个模型,所以我提供下面的代码:

Person.cs:

using System;
using System.Collections.Generic;

namespace MyApp.Models
{
    public partial class Person
    {
        public Person()
        {
            Profiles= new HashSet<Profile>();
        }

        public int Id { get; set; }
        public string Email { get; set; } = null!;
        public string Password { get; set; } = null!;

        public virtual ICollection<Profile> Profiles { get; set; }
    }
}

Profile.cs:

using System;
using System.Collections.Generic;

namespace MyApp.Models
{
    public partial class Profile
    {
        public Profile()
        {
            Sales = new HashSet<Sale>();
        }

        public int Id { get; set; }
        public int PersonId { get; set; }
        public string ProfileName { get; set; } = null!;

        public virtual Person Person { get; set; } = null!;
        public virtual ICollection<Sale> Sales { get; set; }
    }
}

在控制器中,我正在尝试以下查询:

var person = await _context.Person
                           .AsNoTracking()
                           .Where(c => c.Email == 'test@testmail.com')
                           .ToListAsync();

这行得通。但以下查询会引发错误:

var profiles = await _context.Person
                             .AsNoTracking()
                             .Where(c => c.Email == 'test@testmail.com')
                             .Include(c => c.Profiles)
                             .ToListAsync();

错误:

System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.

Path:
$.Profiles.Person.Profiles.Person.Profiles.PersonProfiles.PersonProfiles.PersonProfiles.PersonProfiles.PersonProfiles.PersonProfiles.Person.Id.

at System.Text.Json.ThrowHelper.ThrowJsonException_SerializerCycleDetected(Int32 maxDepth)
at System.Text.Json.Serialization.JsonConverter1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)

你得到这个的原因是因为你正在序列化一个有循环的对象图,这是典型的 EF DB 实体图

假设你有一个人有一个个人资料,那个个人资料有一个人(link 回到父人)

可以像这样编写 C# 代码:

myPerson.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person.Profiles.First().Person..

你可以永远这样下去..

..这就是 json 序列化程序在尝试序列化每个配置文件 属性 时也在做的事情

你有几个选择..

  • 您可以在序列化之前修改图表:将每个配置文件的人物(或第 N 属性 级别的第一个 属性 设置为 link回到较早的 N-x 个)到 null 以停止循环

  • 您可以序列化没有循环的对象图,例如在序列化之前将 EF 实体映射到的一组 DTO

  • 您可以告诉 Newtonsoft 查看参考资料,不要设置它之前看到的任何内容:

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

我说,大多数人会避免看到这个,因为他们会选择选项 2;通常我们不序列化我们的数据库实体,我们有一组 类 专门为我们的前端使用而设计,它们可能看起来不同等;系统的前端很少完全准确地需要看起来与数据库建模完全一样的数据对象。如果那些以前端为重点的对象没有循环(一个人可能有一个配置文件列表,但不需要将配置文件返回 link,因为它在绘制 hi 时用得太低了)他们不要引起这个问题。

注意选项 1;如果在将图形修改为序列化程序友好后调用上下文(出于任何原因)保存,则最终可能会断开数据库中的关系。 3有点麻烦,解决了没有做2导致的问题,但是2更努力去