2

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.

Valuator
  • 3,262
  • 2
  • 29
  • 53

1 Answers1

6

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);
    
amartynov
  • 4,125
  • 2
  • 31
  • 35
  • I've found that when using these DevExtreme components the "subset of properties" approach selecting only what you need is the best method. These controls serialize the entire object bound to them regardless of if its actually used resulting in huge server responses sometimes. – Valuator Feb 27 '18 at 16:36