来自 Newtonsoft JsonSerializer 使用 Entity Framework Core 的自引用循环
Self referencing loop from Newtonsoft JsonSerializer using Entity Framework Core
我遇到了错误:
JsonSerializationException: Self referencing loop detected for
property 'Subject' with type 'Project.Models.Subject'. Path
'data[0].Totals'.
当我加载一个包含由 IEnumerable<Subject>
模型填充的 dataGrid 的视图时,会发生这种情况。网格是绑定到视图模型的 DevExtreme DataGrid,如下所示:
@(Html.DevExtreme().DataGrid()
.DataSource(Model)
.Paging(paging =>
{
paging.Enabled(true);
paging.PageIndex(0);
paging.PageSize(20);
})
.Columns(columns =>
{
columns.Add().DataField("SubjectId");
... other fields
})
)
它是从使用此功能从存储库中提取数据的控制器填充的:
public async Task<IEnumerable<Subject>> GetSubjectsAsync()
{
return await _context.Subject.ToListAsync();
}
主题 table 与总计具有 1:1 关系,总计具有对主题的外键引用。项目中的模型如下所示(从 Scaffold-DbContext 生成):
public partial class Subject
{
public Guid SubjectId { get; set; }
public virtual Totals Totals { get; set; }
}
public partial class Totals
{
public Guid TotalsId { get; set; }
public virtual Subject Subject { get; set; }
}
由于这 2 个对象相互引用,因此在序列化时会导致循环。为了纠正这个问题,我将这个配置添加到我的 Startup.ConfigureServices 方法中:
services.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
我从这个答案中得到的:
然而,这并不能解决问题,当我加载涉及主题的视图时,它仍然会导致错误。将 [JsonIgnore]
添加到 Totals 的主题 属性 可以解决问题,但我不想将其添加到我模型中的每个子项 属性 并且每次更新时都必须重做我的数据库模型。
JSON 序列化期间 self-referencing 循环的问题与 EFCore 如何加载相关数据 (docs) 有关。当您加载集合时,相关实体可能会自动填充,也可能不会自动填充,具体取决于之前是否已加载这些对象。它被称为 自动 fix-up 导航属性 。或者,可以通过 .Include()
.
急切加载它们
每当您获得要序列化的 self-referencing 个实体图时,有几个选项:
Newtonsoft.Json.ReferenceLoopHandling.Ignore
(officially recommended)。它可以工作,但如果 self-reference 发生在层次结构的深处,仍然会导致序列化过多的数据。
[JsonIgnore]
导航属性上的属性。如您所述,重新生成模型 类 时属性会消失。因此,它们的使用可能会很不方便。
(最佳选择) Pre-select 属性子集:
var minimallyNecessarySet= _nwind.Products.Select(p => new {
p.ProductID,
p.ProductName,
p.UnitPrice,
p.CategoryID
});
return minimallyNecessarySet.ToList();
这种方法的优点是只序列化需要的数据。它兼容 DevExtreme 的 DataSourceLoader
:
return DataSourceLoader.Load(minimallyNecessarySet, loadOptions);
我遇到了错误:
JsonSerializationException: Self referencing loop detected for property 'Subject' with type 'Project.Models.Subject'. Path 'data[0].Totals'.
当我加载一个包含由 IEnumerable<Subject>
模型填充的 dataGrid 的视图时,会发生这种情况。网格是绑定到视图模型的 DevExtreme DataGrid,如下所示:
@(Html.DevExtreme().DataGrid()
.DataSource(Model)
.Paging(paging =>
{
paging.Enabled(true);
paging.PageIndex(0);
paging.PageSize(20);
})
.Columns(columns =>
{
columns.Add().DataField("SubjectId");
... other fields
})
)
它是从使用此功能从存储库中提取数据的控制器填充的:
public async Task<IEnumerable<Subject>> GetSubjectsAsync()
{
return await _context.Subject.ToListAsync();
}
主题 table 与总计具有 1:1 关系,总计具有对主题的外键引用。项目中的模型如下所示(从 Scaffold-DbContext 生成):
public partial class Subject
{
public Guid SubjectId { get; set; }
public virtual Totals Totals { get; set; }
}
public partial class Totals
{
public Guid TotalsId { get; set; }
public virtual Subject Subject { get; set; }
}
由于这 2 个对象相互引用,因此在序列化时会导致循环。为了纠正这个问题,我将这个配置添加到我的 Startup.ConfigureServices 方法中:
services.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
我从这个答案中得到的:
然而,这并不能解决问题,当我加载涉及主题的视图时,它仍然会导致错误。将 [JsonIgnore]
添加到 Totals 的主题 属性 可以解决问题,但我不想将其添加到我模型中的每个子项 属性 并且每次更新时都必须重做我的数据库模型。
JSON 序列化期间 self-referencing 循环的问题与 EFCore 如何加载相关数据 (docs) 有关。当您加载集合时,相关实体可能会自动填充,也可能不会自动填充,具体取决于之前是否已加载这些对象。它被称为 自动 fix-up 导航属性 。或者,可以通过 .Include()
.
每当您获得要序列化的 self-referencing 个实体图时,有几个选项:
Newtonsoft.Json.ReferenceLoopHandling.Ignore
(officially recommended)。它可以工作,但如果 self-reference 发生在层次结构的深处,仍然会导致序列化过多的数据。[JsonIgnore]
导航属性上的属性。如您所述,重新生成模型 类 时属性会消失。因此,它们的使用可能会很不方便。(最佳选择) Pre-select 属性子集:
var minimallyNecessarySet= _nwind.Products.Select(p => new { p.ProductID, p.ProductName, p.UnitPrice, p.CategoryID }); return minimallyNecessarySet.ToList();
这种方法的优点是只序列化需要的数据。它兼容 DevExtreme 的
DataSourceLoader
:return DataSourceLoader.Load(minimallyNecessarySet, loadOptions);