web-dev-qa-db-fra.com

Entity Framework chargeant la collection enfant avec l'ordre de tri

J'ai deux tables un parent et une table enfant. La table enfant a un ordre de tri de colonne (une valeur numérique). En raison de la prise en charge manquante de l'EF pour conserver une IList incluant l'ordre de tri sans exposer l'ordre de tri (voir: Entity Framework persistant l'ordre de tri de la collection enfant ) ma classe enfant a également une propriété SortOrder, de sorte que je peut stocker les enfants avec l'ordre de tri.

Contrairement à l'auteur de la question référencée, j'essaie de charger les enfants toujours triés. Donc, si je charge une instance parent, je m'attends à ce que la collection enfant soit triée par ordre de tri. Comment puis-je obtenir ce comportement avec l'API Code First Fluent et les POCO?

Astuce: Ce n'est pas une option pour appeler .Sort (...) sur la collection enfant.

32
X181

Vous ne pouvez pas y parvenir directement car aucun chargement rapide ou paresseux dans EF ne prend en charge la commande ou le filtrage.

Vos options sont:

  • Trier les données dans votre application après les avoir chargées à partir de la base de données
  • Exécutez une requête distincte pour charger les enregistrements enfants. Une fois que vous utilisez une requête distincte, vous pouvez utiliser OrderBy

La deuxième option peut être utilisée avec un chargement explicite:

var parent = context.Parents.First(...);
var entry = context.Entry(parent);
entry.Collection(e => e.Children)
     .Query()
     .OrderBy(c => c.SortOrder)
     .Load();
38
Ladislav Mrnka

Vous pouvez le faire efficacement en une seule requête, la grammaire est juste maladroite:

var groups = await db.Parents
    .Where(p => p.Id == id)
    .Select(p => new
        {
            P = p,
            C = p.Children.OrderBy(c => c.SortIndex)
        })
    .ToArrayAsync();

// Query/db interaction is over, now grab what we wanted from what was fetched

var model = groups
    .Select(g => g.P)
    .FirstOrDefault();

Explication

note asynchrone

Il m'est arrivé d'utiliser les extensions async ici, que vous devriez probablement utiliser, mais vous pouvez vous débarrasser de await/async si vous avez besoin d'une requête synchrone sans nuire à l'efficacité tri des enfants.

Premier morceau

Par défaut, tous les objets EF extraits de la base de données sont "suivis". De plus, l'équivalent d'EF à SQL Select est conçu autour des objets anonymes, que vous nous voyez sélectionner ci-dessus. Lorsque l'objet anonyme est créé, les objets affectés à P et C sont tous les deux suivis, ce qui signifie que leurs relations sont notées et leur état est maintenu par EF Change Tracker. Puisque C est une liste d'enfants dans P, même si vous ne leur avez pas demandé d'être liés explicitement dans votre objet anonyme, EF les charge de toute façon comme cette collection enfant, à cause de la relation qu'il voit dans le schéma.

Pour en savoir plus, vous pouvez diviser ce qui précède en 2 requêtes distinctes, en chargeant uniquement l'objet parent, puis uniquement la liste des enfants, dans des appels Db complètement différents. EF Change Tracker remarquera et chargera les enfants dans l'objet parent pour vous.

Deuxième morceau

Nous avons trompé EF pour qu'il retourne les enfants commandés. Maintenant, nous prenons juste l'objet Parent - ses enfants seront toujours attachés dans l'ordre comme nous le voulions.

Nuls et tables sous forme d'ensembles

Il y a 2 étapes maladroites ici, principalement pour les meilleures pratiques concernant les valeurs nulles; il est là pour faire 2 choses:

  • Considérez les choses dans la base de données comme des ensembles jusqu'au dernier moment absolu possible.

  • Évitez les exceptions nulles.

En d'autres termes, le dernier morceau aurait pu être:

var model = groups.First().P;

Mais si l'objet n'était pas présent dans la base de données, cela explosera avec une exception de référence nulle. C # 6 introduira cependant une autre alternative, l'opérateur de coalescence des propriétés nulles - donc à l'avenir, vous pourrez remplacer le dernier morceau par:

var model = groups.FirstOrDefault()?.P;
28
Chris Moschini