Self referencing loop from Newtonsoft JsonSerializer using Entity Framework Core 使用Entity Framework Core时,从Newtonsoft JsonSerializer引发自引用循环。

20 浏览
0 Comments

Self referencing loop from Newtonsoft JsonSerializer using Entity Framework Core 使用Entity Framework Core时,从Newtonsoft JsonSerializer引发自引用循环。

我遇到了以下错误:

JsonSerializationException:检测到自引用循环'Property 'Subject' with type 'Project.Models.Subject'。路径'data[0].Totals'。

当我加载一个由IEnumerable模型填充的数据网格的视图时发生了这个错误。该网格是一个绑定到视图模型的DevExtreme DataGrid,如下所示:

@(Html.DevExtreme().DataGrid()
    .DataSource(Model)
    .Paging(paging =>
    {
        paging.Enabled(true);
        paging.PageIndex(0);
        paging.PageSize(20);
    })
    .Columns(columns =>
    {
        columns.Add().DataField("SubjectId");
        ...其他字段
    })
)

该视图从一个从存储库中获取数据的控制器填充,该控制器使用以下函数:

public async Task> GetSubjectsAsync()
        {
            return await _context.Subject.ToListAsync();
        }

Subject表与Totals表具有1:1的关系,Totals表具有对Subject的外键引用。项目中的模型如下所示(从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; }
    }

由于这两个对象相互引用,导致在序列化时产生循环。为了解决这个问题,我在我的Startup.ConfigureServices方法中添加了以下配置:

services.AddMvc()
                .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

我从这个答案中得到了这个配置:https://stackoverflow.com/a/40501464/7897176

然而,这并没有解决问题,当我加载涉及Subjects的视图时仍然会出错。将[JsonIgnore]添加到Totals的Subject属性可以解决这个问题,但是我不想在我的模型的每个子属性中都添加它,并且在从数据库更新模型时不想重复这样做。

0
0 Comments

在使用Entity Framework Core进行JSON序列化时,出现了自引用循环的问题。这个问题与EFCore加载相关数据的方式有关。当加载一个集合时,相关实体可能会自动填充,具体取决于这些对象是否已经被先前加载过。这被称为导航属性的自动修复。或者,可以通过`.Include()`方法来主动加载它们。

当你需要序列化一个自引用实体的图形时,有几种解决方法:

- `Newtonsoft.Json.ReferenceLoopHandling.Ignore`(官方推荐):这个方法可以解决问题,但如果自引用出现在层次结构的深处,仍然会导致序列化过多的数据。

- 在导航属性上使用`[JsonIgnore]`属性:正如你所注意到的,当模型类重新生成时,属性会消失。因此,使用这种方法可能不太方便。

- 最佳选择是预先选择一组子集属性:通过选择需要的属性来进行序列化,例如:

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);

我发现,在使用这些DevExtreme组件时,选择只包含所需属性的子集方法是最好的方法。这些控件会序列化绑定给它们的整个对象,而不管它是否真正被使用,这可能导致服务器响应非常庞大。

0