6 votes

Inclure une propriété mais exclure l'une des propriétés de cette propriété

Supposons que j'ai une méthode comme celle-ci dans l'un de mes contrôleurs :

[Route("api/Produits")]
public IQueryable GetProduits() {
    return db.Produits
             .Include(p => p.Categorie);
}

Avec cela, je peux obtenir un produit de la base de données et inclure sa propriété Catégorie.

Dans mon CategoryController j'ai cette méthode :

[Route("api/Categories")]
public IQueryable GetCategories() {
    return db.Categories
             .Include(c => c.Parent)
             .Include(c => c.Produits)
             .Include(c => c.SousCategories);
}

Lorsque j'envoie une requête GET au CategoryController, cela fonctionne comme prévu, j'obtiens la catégorie, son parent, ses produits et ses sous-catégories. Mais lorsque j'envoie une requête GET au ProductController, je ne veux pas inclure tous les produits de la catégorie du produit demandé, j'ai juste besoin des informations de base sur cette catégorie.

Alors, comment puis-je faire en sorte que GetProduits() renvoie les produits de la base de données, en incluant la propriété Catégorie de chaque produit, mais en excluant la propriété liste Produits de la catégorie, tout en conservant les autres propriétés comme l'id, le titre, etc. ?

Merci.

5voto

Gert Arnold Points 27642

Comme indiqué dans les commentaires, la première étape consiste à désactiver le chargement paresseux. Vous pouvez le faire en supprimant le modificateur virtual des propriétés de collection, ce qui est permanent, ou en le désactivant par instance de contexte, ce qui est temporaire :

context.Configuration.ProxyCreationEnabled = false;

(désactiver la création de proxy désactive également le chargement paresseux, mais rend les objets générés plus légers).

Dans les scénarios déconnectés, comme Web AIP2 (comme vous l'avez initialement tagué la question), les gens préfèrent souvent désactiver le chargement paresseux par défaut, en raison de cette cascade sérialiseur-chargement paresseux.

Cependant, vous ne pouvez pas arrêter Entity Framework d'exécuter la résolution des associations. Charger un Produit le lie au contexte. En incluant ses catégories, vous les liez au contexte et EF remplit leurs collections de Produits avec le produit attaché, que vous le vouliez ou non.

Vous pouvez réduire quelque peu cet effet en récupérant les produits avec AsNoTracking (ce qui empêche les entités d'être attachées, c'est-à-dire suivies de leur évolution) :

return db.Produits.AsNoTracking()
         .Include(p => p.Catégorie);

Maintenant, les catégories auront uniquement leurs Produits remplis des Produits dont ils sont la catégorie.

En passant, dans les scénarios déconnectés, l'utilisation de AsNoTracking est également préférable. Les entités ne seront de toute façon jamais enregistrées par la même instance de contexte et cela améliore les performances.

Solutions

  • Retourner des DTO, pas des types d'entité

En utilisant des objets DTO, vous avez un contrôle total sur le graphique d'objets qui sera sérialisé. Le chargement paresseux ne vous surprendra pas. Mais oui, la quantité de classes DTO requises peut être écrasante.

  • Retourner des types anonymes.

Cela peut surprendre car nous ne devrions jamais retourner de types anonymes à partir de méthodes, n'est-ce pas ? Eh bien, ils quittent une méthode d'action sous forme de chaîne Json, tout comme les types nommés, et le client JavaScript ne fait pas la distinction. On pourrait dire que cela rapproche l'environnement JavaScript faiblement typé d'un pas. La seule chose est qu'un type DTO nommé sert de contrat de données (pour ainsi dire) et les types anonymes peuvent être modifiés (trop) facilement et casser le code côté client. Mais nous testons toujours tout unitairement, n'est-ce pas ? Hmm, c'est une option viable dans les petites équipes de développement.

  • Personnaliser le sérialiseur.

Vous pouvez indiquer au sérialiseur Json.Net d'ignorer les boucles de référence. En utilisant JsonConvert directement, cela ressemble à ceci :

var produits = db.Produits.AsNoTracking().Include(p => p.Catégorie);
var paramètre = new JsonSerializerSettings
{
    Formatting = Newtonsoft.Json.Formatting.Indented, // Juste pour les humains
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
var json = JsonConvert.SerializeObject(produits, paramètre);

En combinant cela avec AsNoTracking(), cela sérialisera les catégories avec des tableaux Produits vides ("Produits": []), car Produit - Catégorie - Produit est une boucle de référence.

Dans Web API, il existe plusieurs façons de configurer le sérialiseur Json.Net intégré, vous voudrez le faire par méthode d'action.

Personnellement, je préfère utiliser des DTO. J'aime avoir le contrôle (aussi sur les propriétés qui traversent le réseau) et je n'aime pas particulièrement dépendre d'un sérialiseur pour résoudre ce que j'ai négligé de faire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X