有人能解释一下这种行为吗?这样我以后就可以管理它了.

我有一个带有许多Include语句的查询(定义为IQueryable).我使用的是SQL Server数据库的Entity框架.

主要实体和几个包含的实体有一个指向代码表的外键,我用它来定义时间段类型(年、月、季度等)以及与这些类型相关的数据.

在Period实体中,我具有它们向其提供值的实体的导航属性.

在成功地运行了包含它的查询之后,我将使用system.ext.json来序列化结果.

尽管将引用处理程序设置为"PERVERE",我还是开始在序列化过程中收到周期错误.

经过调查,我发现序列化是成功还是失败取决于My Includes在代码表实体中的顺序.

因此,如果我的实体包含如下导航属性:

public IList<entity1>? entity1Collection { get; set; } = new List<entity1>();
public IList<entity2>? entity2Collection { get; set; } = new List<entity3>();
public IList<entity3>? entity3Collection { get; set; } = new List<entity3>();
public IList<entity4>? entity4Collection { get; set; } = new List<entity4>();
public IList<entity5>? entity5Collection { get; set; } = new List<entity5>();

结果:序列化工作正常.

但是,如果我交换了上述导航的顺序(通过剪切粘贴),序列化将会因为循环错误而失败.

*注:以上我的实体编号方案仅用于本帖子的目的,没有任何意义.

因为我不相信顺序很重要,所以我反复从头开始重新执行,总是获得相同的结果--我相信没有引入其他不同之处.

寻求帮助理解这一点:

  1. 这是预期中的行为吗?
  2. 如果是,在我的实体中定义导航属性的顺序时,有没有我应该遵循的规则/指南?

以下是错误:

System.Text.Json.JsonException:‘检测到可能的对象周期.这可能是由于周期引起的,也可能是对象深度大于允许的最大深度64.请考虑在JsonSerializerOptions上使用ReferenceHandler.PReserve来支持周期.路径:

而且,是的,这是一个MaxDepth个问题.

推荐答案

Json是一个单遍深度优先的串行化程序.当启用reference preservation时,对象将在遇到first time时被序列化.只有"$ref": "N"会在随后的遭遇中被序列化.因此,更改属性顺序可能会导致超过MaxDepth.

例如,考虑下面的模型,它包含一个父级列表和一个所有子级列表.父母有子元素,子元素有朋友:

public class Model
{
    public List<Child> AllChildren { get; set; }= new();
    public List<Parent> Parents { get; set; }= new();
}

public class Parent
{
    public List<Child> Children { get; set; } = new();
}

public class Child
{
    public List<Child> Friends { get; set; } = new ();
}

如果我构造一个包含2个父代和2个子代的实例,其中每个子代都是朋友:

var child1 = new Child { };
var child2 = new Child { Friends = { child1 } };
child1.Friends.Add(child2);

var model = new TModel
{
    AllChildren = { child1, child2 },
    Parents = { new Parent { Children = { child1 } }, new Parent { Children = { child2 } } },
};

则该模型将在MaxDepth = 10处成功串行化,并在MaxDepth = 9处失败.演示小提琴#1here.

现在假设我修改了Model以首先序列化父级:

public class Model
{
    public List<Parent> Parents { get; set; }= new();
    public List<Child> AllChildren { get; set; }= new();
}

然后是serialization will fail at 101 10, 11 and 12.它将仅从MaxDepth = 13开始成功串行化.演示小提琴#2here.

造成差异的原因是前面提到的深度优先序列化.在第一个版本中,第二个子元素被序列化为AllChildren列表中第一个子元素的朋友:

{
  "$id": "1",
  "AllChildren": {
    "$id": "2",
    "$values": [
      {
        "$id": "3",
        "Friends": {
          "$id": "4",
          "$values": [
            {
              "$id": "5",
              "Friends": {
                "$id": "6", // Here is where the second child is serialized.
                "$values": [
                  {
                    "$ref": "3"
                  }
                ]
              }
            }
          ]
        }
      },
      {
        "$ref": "5"
      }
    ]
  },
  "Parents": {
    "$id": "7",
    "$values": [
      {
        "$id": "8",
        "Children": {
          "$id": "9",
          "$values": [
            {
              "$ref": "3"
            }
          ]
        }
      },
      {
        "$id": "10",
        "Children": {
          "$id": "11",
          "$values": [
            {
              "$ref": "5"
            }
          ]
        }
      }
    ]
  }
}


但在第二个版本中,第二个子级在Parents列表内被序列化,导致最大深度增加:

Testing MaxDepth = 13:
{
  "$id": "1",
  "Parents": {
    "$id": "2",
    "$values": [
      {
        "$id": "3",
        "Children": {
          "$id": "4",
          "$values": [
            {
              "$id": "5",
              "Friends": {
                "$id": "6",
                "$values": [
                  {
                    "$id": "7",
                    "Friends": {
                      "$id": "8", // Here is where the second child is serialized.  Notice it's deeper in the graph.
                      "$values": [
                        {
                          "$ref": "5" 
                        }
                      ]
                    }
                  }
                ]
              }
            }
          ]
        }
      },
      {
        "$id": "9",
        "Children": {
          "$id": "10",
          "$values": [
            {
              "$ref": "7"
            }
          ]
        }
      }
    ]
  },
  "AllChildren": {
    "$id": "11",
    "$values": [
      {
        "$ref": "5"
      },
      {
        "$ref": "7"
      }
    ]
  }
}

To prevent this,您可以将JsonPropertyOrderAttribute应用于您的属性,以强制在父级之前序列化子级:

public class Model
{
    [JsonPropertyOrder(2)]
    public List<Parent> Parents { get; set; }= new();
    [JsonPropertyOrder(1)]
    public List<Child> AllChildren { get; set; }= new();
}

如果这样做,序列化将不再依赖于C#属性顺序.演示#3 here.

Csharp相关问答推荐

Umbraco Lucene日期字段编码转换为.net日期时间

如果第一个匹配项为空,则规则运算不会拆分C#中分离字符串上的子菜单

亚马逊Pinpoint C# SDK

在C#WinUI中,一个关于System的崩溃."由于未知原因导致执行不例外"

如何删除文件的基础上嵌入的时间戳嵌入文件名

如何在Windows 11任务调度程序中每1分钟重复一次任务?

共享暂存/生产环境中Azure事件中心的建议配置

如何捕获对ASP.NET核心应用程序的所有请求并将其发送到一个页面

反序列化私有成员

确定System.Text.Json序列化中是否无法识别Type

为什么我可以用硬编码的集合而不是变量来设置没有setter的IList属性的值?

在.NET 8最低API中从表单绑定中排除属性

异步等待Foreach循环中的ConfigureAWait(FALSE)执行什么操作?

是否由DI容器自动处理由ActivatorUilties.CreateInstance()创建的服务?

在等待OnGetAsync时打开Razor Page显示微调器

ASP.NET MVC数据批注验证组复选框

仅在ASP.NETCore应用程序中的附加单独端口上公开一组终结点

如何在特定时间间隔运行多个后台任务?

用于分钟和秒验证的MudTextfield的正则表达式掩码

嵌套Blazor组件内的验证