Ho riscontrato l'errore:
JsonSerializationException: Rilevato loop di riferimento automatico per la proprietà "Subject" con tipo "Project.Models.Subject". Path 'data [0] .Totals'.
Si verifica quando carico una vista con un dataGrid popolato da un modello IEnumerable<Subject>
. The Grid è un DataGrid DevExtreme associato al modello di View in questo modo:
@(Html.DevExtreme().DataGrid()
.DataSource(Model)
.Paging(paging =>
{
paging.Enabled(true);
paging.PageIndex(0);
paging.PageSize(20);
})
.Columns(columns =>
{
columns.Add().DataField("SubjectId");
... other fields
})
)
Che viene popolato da un controller che estrae i dati da un repository con questa funzione:
public async Task<IEnumerable<Subject>> GetSubjectsAsync()
{
return await _context.Subject.ToListAsync();
}
La tabella Oggetto ha una relazione 1: 1 con Totali con Totali aventi un riferimento a chiave esterna a Soggetto. I modelli nel progetto sono simili a questo (generati da 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; }
}
Poiché i 2 oggetti si riferiscono a vicenda, provoca un loop durante la serializzazione. Per correggere questo ho aggiunto questa configurazione al mio metodo Startup.ConfigureServices:
services.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Quale ho ottenuto da questa risposta: https://stackoverflow.com/a/40501464/7897176
Tuttavia questo non risolve il problema e causa ancora un errore quando carico una vista che coinvolge Soggetti. L'aggiunta di [JsonIgnore]
alla proprietà Subject of Totals risolve il problema, ma non voglio [JsonIgnore]
aggiungere a tutte le proprietà child nei miei modelli e doverlo ripetere ogni volta che aggiorno i miei modelli dal db.
Il problema dei loop autoreferenziali durante la serializzazione JSON è collegato al modo in cui EFCore carica i dati correlati ( documenti ). Quando si carica una raccolta, le entità correlate possono essere popolate automaticamente o meno a seconda che tali oggetti siano stati precedentemente caricati o meno. Si chiama correzione automatica delle proprietà di navigazione . In alternativa, possono essere caricati con .Include()
tramite .Include()
.
Ogni volta che ottieni un grafico autoreferenziale delle entità da serializzare, ci sono diverse opzioni:
Newtonsoft.Json.ReferenceLoopHandling.Ignore
( ufficialmente raccomandato ). Funziona ma può ancora generare serializzazione di dati eccessivi se l'autoriferimento si verifica in profondità nella gerarchia.
[JsonIgnore]
attributo sulle proprietà di navigazione. Come hai notato, gli attributi scompaiono quando le classi del modello vengono rigenerate. Pertanto, il loro uso può essere sconveniente.
(Scelta migliore) Preselezionare un sottoinsieme di proprietà:
var minimallyNecessarySet= _nwind.Products.Select(p => new {
p.ProductID,
p.ProductName,
p.UnitPrice,
p.CategoryID
});
return minimallyNecessarySet.ToList();
Questo approccio ha il vantaggio di serializzare solo i dati richiesti. È compatibile con DataSourceLoader
di DevExtreme:
return DataSourceLoader.Load(minimallyNecessarySet, loadOptions);