È stato rilevato un possibile ciclo di oggetti .Net Core 3.0 che non è supportato


22

Ho 2 entità che sono correlate come una a molte

public class Restaurant {
   public int RestaurantId {get;set;}
   public string Name {get;set;}
   public List<Reservation> Reservations {get;set;}
   ...
}
public class Reservation{
   public int ReservationId {get;set;}
   public int RestaurantId {get;set;}
   public Restaurant Restaurant {get;set;}
}

Se provo a trovare ristoranti con prenotazioni usando la mia API

   var restaurants =  await _dbContext.Restaurants
                .AsNoTracking()
                .AsQueryable()
                .Include(m => m.Reservations).ToListAsync();
    .....

Ricevo un errore in risposta, perché gli oggetti contengono riferimenti reciproci. Ci sono post correlati che consigliano di creare un modello separato o aggiungere la configurazione NewtonsoftJson

Il problema è che non voglio creare un modello separato e il secondo suggerimento non ha aiutato. Esiste un modo per caricare i dati senza relazione ciclica? *

System.Text.Json.JsonException: è stato rilevato un possibile ciclo di oggetti che non è supportato. Ciò può essere dovuto a un ciclo o se la profondità dell'oggetto è maggiore della profondità massima consentita di 32. su System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (Int32 maxDepth) su System.Text.Json.JsonSerializer.Write (Utf8Json writer , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions opzioni, WriteStack & state) in System.Text.Json.JsonSerializer.WriteAsyncCore (Stream utf8Json, Valore oggetto, Tipo inputType, JsonSerializerOptions.Proc. WriteResponseBodyAsync (contesto OutputFormatterWriteContext, Encoding selectedEncoding) su Microsoft.AspNetCore.Mvc.

*


Chiedere di ignorare la proprietà Restaurant della classe Reservation.
Lasse V. Karlsen,

6
Davvero non dovresti restituire le tue entità DB direttamente dalla tua API. Suggerirei di creare DTO specifici per API e di mappare di conseguenza. È vero che hai detto che non volevi farlo, ma prenderei in considerazione una buona pratica generale per mantenere separati gli interni di API e persistenza.
Mackie,

"e il secondo suggerimento non ha aiutato" ha bisogno di dettagli.
Henk Holterman,

"Il problema è che non voglio creare un modello separato". Il tuo design è fondamentalmente imperfetto a meno che tu non lo faccia. Un'API è un contratto come un'interfaccia (è letteralmente una programmazione di applicazioni di interfaccia ). Non dovrebbe mai cambiare, una volta pubblicato, e qualsiasi modifica richiede una nuova versione, che dovrà essere eseguita contemporaneamente alla vecchia versione (che sarà deprecata e infine rimossa in futuro). Ciò consente ai clienti di aggiornare le loro implementazioni. Se restituisci direttamente un'entità, stai strettamente accoppiando il tuo livello dati.
Chris Pratt,

Qualsiasi modifica a quel livello dati richiede quindi una modifica immediata e irreversibile all'API, interrompendo immediatamente tutti i client fino a quando non aggiornano le loro implementazioni. Nel caso in cui non sia ovvio, è una brutta cosa. In breve: non accettare o restituire mai entità da un'API. Dovresti sempre usare DTO.
Chris Pratt,

Risposte:


32

Ho provato il tuo codice in un nuovo progetto e il secondo modo sembra funzionare bene dopo aver installato il pacchetto Microsoft.AspNetCore.Mvc.NewtonsoftJson in primo luogo per 3.0

services.AddControllerWithViews()
    .AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

Prova con un nuovo progetto e confronta le differenze.


1
Il momento chiave qui è reinstallare la versione corretta di Microsoft.AspNetCore.Mvc.NewtonsoftJson Non ho prestato attenzione alla versione in quanto questo pacchetto era disponibile sotto la scatola senza errori e avvertenze! Grazie per la risposta ! Tutto funziona esattamente come mi aspettavo!
Nazar Pylyp,

1
Non è sbagliato che, con una migliore perf del sistema json, dobbiamo usare NewtonsoftJson? : /
Marek Urbanowicz

40

.NET Core 3.1 Installa il pacchetto Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Aggiungi servizio

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

1
Puoi formattare la tua risposta e aggiungere alcuni dettagli? È illeggibile.
Sid

Per maggiori dettagli, consultare: thecodebuzz.com/…
Diego Venâncio il

4

Far funzionare l'impostazione delle opzioni di serializzazione JSON all'avvio è probabilmente un modo preferito in quanto probabilmente si avranno casi simili in futuro. Nel frattempo, tuttavia, puoi provare ad aggiungere attributi di dati al tuo modello, quindi non è serializzato: https://www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm

public class Reservation{ 
    public int ReservationId {get;set;} 
    public int RestaurantId {get;set;} 
    [JsonIgnore]
    public Restaurant Restaurant {get;set;} 
}

Anche questo funziona. Ma come hai detto, con questo devi aggiornare tutti i modelli, preferisco services.AddControllers (). AddNewtonsoftJson (options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Nantharupan,

1
public class Reservation{ 
public int ReservationId {get;set;} 
public int RestaurantId {get;set;} 
[JsonIgnore]
public Restaurant Restaurant {get;set;} 

Sopra ha funzionato anche. Ma preferisco quanto segue

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

Poiché prima dobbiamo aggiungere l'attributo a tutti i modelli, potremmo avere il riferimento ciclico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.