Le cycle d'objet possible de .Net Core 3.0 a été détecté, ce qui n'est pas pris en charge


22

J'ai 2 entités liées comme une à plusieurs

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

Si j'essaie d'obtenir des restaurants avec des réservations en utilisant mon API

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

Je reçois une erreur en réponse, car les objets contiennent des références les uns aux autres. Il existe des articles connexes qui recommandent de créer un modèle distinct ou d'ajouter une configuration NewtonsoftJson

Le problème est que je ne veux pas créer de modèle séparé et la deuxième suggestion n'a pas aidé. Existe-t-il un moyen de charger des données sans relation cyclée? *

System.Text.Json.JsonException: un éventuel cycle d'objet a été détecté qui n'est pas pris en charge. Cela peut être dû à un cycle ou si la profondeur de l'objet est supérieure à la profondeur maximale autorisée de 32. sur System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (Int32 maxDepth) sur System.Text.Json.JsonSerializer.Write (Utf8JsonWriter writer , Int32 originalWriterDepth, Int32 flushThreshold, options JsonSerializerOptions, WriteStack & state) sur System.Text.Json.JsonSerializer.WriteAsyncCore (Stream utf8Json, valeur d'objet, type inputType, options JsonSerializerOptions, CancellationToken annulationToken) sur Microsoft.AspFormTormFormer WriteResponseBodyAsync (contexte OutputFormatterWriteContext, Encoding selectedEncoding) sur Microsoft.AspNetCore.Mvc.

*


Demandez-lui d'ignorer la propriété Restaurant de la classe Réservation.
Lasse V. Karlsen

6
Vraiment, vous ne devriez pas renvoyer vos entités DB directement à partir de votre API. Je suggère de créer des DTO spécifiques à l'API et de les mapper en conséquence. Certes, vous avez dit que vous ne vouliez pas faire cela, mais je considérerais que c'est une bonne pratique générale de séparer les internes d'API et de persistance.
mackie

"et la 2ème suggestion n'a pas aidé" a besoin de détails.
Henk Holterman

"Le problème est que je ne veux pas créer de modèle séparé". Votre conception est fondamentalement imparfaite à moins que vous ne fassiez exactement cela. Une API est un contrat comme une interface (c'est littéralement une interface de programmation d' application ). Il ne devrait jamais changer, une fois publié, et tout changement nécessite une nouvelle version, qui devra s'exécuter simultanément avec l'ancienne version (qui sera obsolète et éventuellement supprimée à l'avenir). Cela permet aux clients de mettre à jour leurs implémentations. Si vous renvoyez une entité directement, vous couplez étroitement votre couche de données.
Chris Pratt

Toute modification de cette couche de données nécessite alors une modification immédiate et irréversible de l'API, interrompant immédiatement tous les clients jusqu'à ce qu'ils mettent à jour leurs implémentations. Au cas où ce ne serait pas évident, c'est une mauvaise chose. En bref: n'acceptez ni ne renvoyez jamais d'entités d'une API. Vous devez toujours utiliser des DTO.
Chris Pratt

Réponses:


32

J'ai essayé votre code dans un nouveau projet et la deuxième façon semble bien fonctionner après l'installation du package Microsoft.AspNetCore.Mvc.NewtonsoftJson d' abord pour 3.0

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

Essayez avec un nouveau projet et comparez les différences.


1
Le moment clé ici est de réinstaller la version appropriée de Microsoft.AspNetCore.Mvc.NewtonsoftJson Je n'ai pas fait attention à la version car ce package était disponible sous la boîte sans aucune erreur ni avertissement! Merci de répondre ! Tout fonctionne exactement comme je m'y attendais!
Nazar Pylyp

1
N'est-il pas faux qu'avec l'amélioration de la performance du système json, nous devons utiliser NewtonsoftJson? : /
Marek Urbanowicz

40

.NET Core 3.1 Installez le package Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Ajouter un service

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

1
Pouvez-vous formater votre réponse et ajouter quelques détails? C'est illisible.
Sid

Pour plus de détails, consultez: thecodebuzz.com/…
Diego Venâncio

4

Obtenir le réglage des options de sérialisation JSON au démarrage est probablement un moyen préféré car vous aurez probablement des cas similaires à l'avenir. En attendant, cependant, vous pouvez essayer d'ajouter des attributs de données à votre modèle afin qu'il ne soit pas sérialisé: 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;} 
}

Cela fonctionne aussi. Mais comme vous l'avez mentionné, avec cela, vous devez mettre à jour tous les modèles, je préfère les 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;} 

Ci-dessus a également fonctionné. Mais je préfère ce qui suit

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

Parce que nous devons d'abord ajouter l'attribut à tous les modèles, nous pouvons avoir la référence cyclique.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.