【问题标题】:System.Text.Json.JsonException: A possible object cycle was detectedSystem.Text.Json.JsonException:检测到可能的对象循环
【发布时间】:2021-08-30 14:44:03
【问题描述】:

我正在开发一个 .Net 博客样式 API 作为练习项目,但遇到了这个错误。我有一个用户类、帖子类和评论类。 Comment 引用 Post 和 User,每个都是一对多的。

这是我的用户类:

 public class User
    {
        //Other properties..
        public List<Post> Posts { get; set; }
        public List<Comment> Comments { get; set; }
    }

我的帖子类:

public class Post
    {
        //Other properties..
        public List<Comment> Comments { get; set; }
        [Required]
        public User CreatedBy { get; set; }
    }

和我的评论类:

  public class Comment
    {
        //Other properties..
        [Required]
        public User CreatedBy { get; set; }
        [Required]
        public Post Post { get; set; }
    }

我正在尝试保存这样的评论(我有从以前的方法填充的 Post.Id 和 CreatedBy.Id)。这就像它按预期插入数据库一样工作,但是我得到了 JsonException。

comment.Post = await _context.Posts.SingleOrDefaultAsync(p => p.Id == comment.Post.Id);
comment.CreatedBy = await _context.Users.FindAsync(comment.CreatedBy.Id);

await _context.Comments.AddAsync(comment);

await _context.SaveChangesAsync();

return comment;

我可以通过添加 Microsoft.AspNetCore.Mvc.NewtonsoftJson 并将其添加到我的启动类来解决此问题

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

但我想知道我是否应该在我的代码中做些什么来避免这种情况,而不是仅仅提供一个选项说没关系。

【问题讨论】:

    标签: c# .net entity-framework-core


    【解决方案1】:

    我的 api (.net 5) 有类似的问题。 API 无法捕获引发的异常 500。要查看实际问题,我必须转到 Windows“事件记录器”。

    为了解决我在配置(startup.cs)下面添加的问题:

       services.AddMvc()
                    .AddJsonOptions(opt =>
                     {
                         opt.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve;
                     });
    

    文档可以在以下位置找到: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to?pivots=dotnet-5-0

    【讨论】:

      【解决方案2】:

      帖子的实例包含评论列表。每个评论实例都包含一个帖子。 Post 的一个实例包含一个 Comments 列表。每个 Comment 实例都包含一个 Post.... 等等。

      虽然这样做并不违反 C# 的规则,但我更愿意避免这样做,因为它会给数据集合带来未知的复杂性。文本序列化是您在执行此操作时会遇到的最大痛点之一。尽管有一些方法可以处理这些痛点(仅举几例,ReferenceLoopHandling 和 Ignore 属性标签),但我发现从我的类定义中尽可能多地省略引用循环要容易得多,即使这意味着定义两个不同命名的类相同的属性,例如 Comment 和 PostComment(其中 PostComment 是包含在帖子下的评论)。

      例如,像下面这样的结构会破坏循环引用并使其易于序列化并持久化到数据库中。注意 Post 的 Comment 属性的类型变化以及 PostComment 中相关的 Post 省略,从而打破循环。

      public class User
      {
          //Other properties..
          public List<Post> Posts { get; set; }
          public List<Comment> Comments { get; set; }
      }
      
      public class Post
      {
          //Other properties..
          public List<PostComment> Comments { get; set; }
          [Required]
          public User CreatedBy { get; set; }
      }
      
      public class Comment
      {
          //Other properties..
          [Required]
          public User CreatedBy { get; set; }
          [Required]
          public Post Post { get; set; }
      }
      
      public class PostComment
      {
          //Other properties..
          [Required]
          public User CreatedBy { get; set; }
      }
      

      【讨论】:

      • 感谢您的回复。你介意给我看一个你的建议后评论的例子吗?
      • EF 在引用循环方面没有问题,因为导航属性是“一等公民”,双向导航是执行多种类型查询的重要部分。因此,不要将 EF 与文本序列化程序放在同一个框中,因为 Chid.ParentParent.Children 是一个典型的对象设计,代表“真正的”生活”的关系。
      • 我从“痛点”参考中删除了 EF。你是对的,它应该能够开箱即用地处理这种关系
      猜你喜欢
      • 2020-07-06
      • 2021-12-09
      • 2020-10-02
      • 1970-01-01
      • 2020-04-27
      • 2021-11-19
      • 1970-01-01
      • 2020-07-26
      • 2021-03-17
      相关资源
      最近更新 更多