.Net Core 3.0 möglicher Objektzyklus wurde erkannt, der nicht unterstützt wird

22

Ich habe 2 Entitäten, die als eins zu viele verwandt sind

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

Wenn ich versuche, Restaurants mit Reservierungen über meine API zu bekommen

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

Ich erhalte eine Fehlerantwort, da Objekte Verweise aufeinander enthalten. Es gibt verwandte Beiträge, die empfehlen , ein separates Modell zu erstellen oder eine NewtonsoftJson-Konfiguration hinzuzufügen

Das Problem ist, dass ich kein separates Modell erstellen möchte und der zweite Vorschlag nicht geholfen hat. Gibt es eine Möglichkeit, Daten ohne zyklische Beziehung zu laden? * *

System.Text.Json.JsonException: Es wurde ein möglicher Objektzyklus erkannt, der nicht unterstützt wird. Dies kann entweder auf einen Zyklus zurückzuführen sein oder wenn die Objekttiefe größer ist als die maximal zulässige Tiefe von 32. bei System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (Int32 maxDepth) bei System.Text.Json.JsonSerializer.Write (Utf8JsonWrit , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions-Optionen, WriteStack & state) bei System.Text.Json.JsonSerializer.WriteAsyncCore (Stream utf8Json, Objektwert, Typ inputType, JsonSerializerOptions-Optionen WriteResponseBodyAsync (OutputFormatterWriteContext-Kontext, Codierung selectedEncoding) bei Microsoft.AspNetCore.Mvc.

* *

Nazar Pylyp
quelle
Bitten Sie es, die Restaurant-Eigenschaft der Reservierungsklasse zu ignorieren.
Lasse V. Karlsen
6
Eigentlich sollten Sie Ihre DB-Entitäten nicht direkt von Ihrer API zurückgeben. Ich würde vorschlagen, API-spezifische DTOs zu erstellen und entsprechend zuzuordnen. Zugegeben, Sie sagten, dass Sie das nicht wollten, aber ich würde es als allgemeine bewährte Methode betrachten, API- und Persistenz-Interna getrennt zu halten.
Mackie
"und 2. Vorschlag hat nicht geholfen" braucht Details.
Henk Holterman
"Problem ist, dass ich kein separates Modell erstellen möchte". Ihr Design ist grundlegend fehlerhaft, wenn Sie nicht genau das tun. Eine API ist ein Vertrag , wie eine Schnittstelle (es ist buchstäblich ein Application Programming Interface ). Es sollte sich niemals ändern, sobald es veröffentlicht wurde, und jede Änderung erfordert eine neue Version, die gleichzeitig mit der alten Version ausgeführt werden muss (die veraltet ist und in Zukunft möglicherweise entfernt wird). Dadurch haben Kunden Zeit, ihre Implementierungen zu aktualisieren. Wenn Sie eine Entität direkt zurückgeben, ist Ihre Datenschicht eng miteinander verbunden.
Chris Pratt
Jede Änderung dieser Datenschicht erfordert dann eine sofortige und irreversible Änderung der API, wodurch alle Clients sofort beschädigt werden, bis sie ihre Implementierungen aktualisieren. Falls es nicht offensichtlich ist, ist das eine schlechte Sache. Kurz gesagt: Akzeptieren oder geben Sie niemals Entitäten von einer API zurück. Sie sollten immer DTOs verwenden.
Chris Pratt

Antworten:

32

Ich habe Ihren Code in einem neuen Projekt ausprobiert und der zweite Weg scheint gut zu funktionieren, nachdem das Paket Microsoft.AspNetCore.Mvc.NewtonsoftJson zunächst für 3.0 installiert wurde

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

Versuchen Sie es mit einem neuen Projekt und vergleichen Sie die Unterschiede.

Ryan
quelle
1
Der entscheidende Moment hier ist die Neuinstallation der richtigen Version von Microsoft.AspNetCore.Mvc.NewtonsoftJson. Ich habe nicht auf die Version geachtet, da dieses Paket ohne Fehler und Warnungen unter der Box verfügbar war! Danke für die Antwort ! Alles funktioniert genau wie ich erwartet hatte!
Nazar Pylyp
1
Ist es nicht falsch, dass wir mit einer verbesserten Leistung des Systems json NewtonsoftJson verwenden müssen? : /
Marek Urbanowicz
40

.NET Core 3.1 Installieren Sie das Paket Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Dienst hinzufügen

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Anjoe
quelle
1
Können Sie Ihre Antwort formatieren und einige Details hinzufügen? Es ist unlesbar.
Sid
Weitere Informationen finden Sie unter: thecodebuzz.com/…
Diego Venâncio
4

Das Einstellen der JSON-Serialisierungsoptionen beim Start ist wahrscheinlich eine bevorzugte Methode, da in Zukunft wahrscheinlich ähnliche Fälle auftreten werden. In der Zwischenzeit können Sie jedoch versuchen, Ihrem Modell Datenattribute hinzuzufügen, damit es nicht serialisiert wird: 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;} 
}
timur
quelle
Das funktioniert auch. Aber wie Sie bereits erwähnt haben, müssen Sie damit alle Modelle aktualisieren. Ich bevorzuge 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;} 

Oben hat auch funktioniert. Aber ich bevorzuge folgendes

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

Da wir zuerst das Attribut zu allen Modellen hinzufügen müssen, haben wir möglicherweise die zyklische Referenz.

Nantharupan
quelle