Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
950 views
in Technique[技术] by (71.8m points)

c# - Self referencing loop from Newtonsoft JsonSerializer using Entity Framework Core

I've encountered the error:

JsonSerializationException: Self referencing loop detected for property 'Subject' with type 'Project.Models.Subject'. Path 'data[0].Totals'.

It occurs when I load a View with a dataGrid populated by an IEnumerable<Subject> model. The Grid is a DevExtreme DataGrid bound to the View's model like this:

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

Which is populated from a Controller that pulls data from a Repository with this function:

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

The Subject table has a 1:1 relationship with Totals with Totals having a foreign key reference to Subject. The Models in the project look like this (generated from 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; }
    }

Since the 2 objects reference eachother it causes a loop when serializing it. To correct this I added this config to my Startup.ConfigureServices method:

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

Which I got from this answer: https://stackoverflow.com/a/40501464/7897176

However this doesn't fix the problem and its still causing an error when I load a view that involves Subjects. Adding [JsonIgnore] to the Subject property of Totals fixes the problem, but I don't want to have to add that to every child property in my models and have to redo it whenever I update my models from the db.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem of self-referencing loops during JSON serialization is connected to how EFCore loads related data (docs). When you load a collection, related entities may or may not be automatically populated depending on whether or not those object have been previously loaded. It's called automatic fix-up of navigation properties. Alternatively, they may be eagerly loaded via .Include().

Whenever you get a self-referencing graph of entities to be serialized, there are several options:

  • Newtonsoft.Json.ReferenceLoopHandling.Ignore (officially recommended). It works but still can result in serializing excessive data if self-reference occurs deep in the hierarchy.

  • [JsonIgnore] attribute on navigation properties. As you noted, attributes disappear when model classes are regenerated. Thus, their use can be inconvenient.

  • (Best choice) Pre-select a subset of properties:

    var minimallyNecessarySet= _nwind.Products.Select(p => new {
        p.ProductID,
        p.ProductName,
        p.UnitPrice,
        p.CategoryID
    });
    
    return minimallyNecessarySet.ToList();
    

    This approach has the advantage of serializing only required data. It's compatible with DevExtreme's DataSourceLoader:

    return DataSourceLoader.Load(minimallyNecessarySet, loadOptions);
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...