40 votes

Entity Framework charge la collection enfant avec un ordre de tri

J'ai deux tables, une table parent et une table enfant. La table enfant a une colonne de tri (une valeur numérique). En raison de l'absence de support de l'EF pour persister 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 peux 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. Ainsi, si je charge une instance parent, je m'attends à ce que la collection d'enfants soit triée par ordre de tri. Comment puis-je obtenir ce comportement avec l'API Fluent et les POCO de Code First ?

Astuce : Il n'est pas possible d'appeler .Sort(...) sur la collection enfant.

50voto

Ladislav Mrnka Points 218632

Vous ne pouvez pas le faire directement car ni le chargement rapide ni le chargement paresseux dans EF ne prennent en charge l'ordonnancement ou le filtrage.

Vous avez le choix :

  • Trier les données dans votre application après les avoir chargées depuis la base de données.
  • Exécuter une requête séparée pour charger les enregistrements enfants. Une fois que vous avez utilisé une requête séparée, 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();

32voto

Chris Moschini Points 7278

Vous pouvez le faire efficacement dans une seule requête, la grammaire est simplement 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 se trouve que j'ai utilisé le async extensions ici, que vous devriez probablement utiliser, mais vous pouvez vous débarrasser des await / async si vous avez besoin d'une requête synchrone sans nuire au tri efficace des enfants.

Premier morceau

Par défaut, tous les objets EF récupérés dans la Db sont "suivis". De plus, l'équivalent d'EF en 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 assignés à l'objet anonyme sont supprimés. P y C sont toutes deux suivies, ce qui signifie que leurs relations sont notées et que leur état est maintenu par l'outil de suivi des changements EF. Depuis C est une liste d'enfants dans P même si vous ne leur avez pas demandé explicitement d'être liés dans votre objet anonyme, EF les charge de toute façon comme cette collection d'enfants, en raison de la relation qu'il voit dans le schéma.

Pour en savoir plus, vous pouvez décomposer la requête ci-dessus en deux requêtes distinctes, en chargeant uniquement l'objet parent, puis uniquement la liste des enfants, dans des appels Db complètement différents. L'outil de suivi des changements EF remarquera et chargera les enfants dans l'objet parent pour vous.

Second morceau

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

Nulles et tables comme ensembles

Il y a une étape maladroite en deux temps, principalement pour des raisons de bonnes pratiques concernant les valeurs nulles ; elle est là pour faire deux choses :

  • Considérez les choses dans le db comme des ensembles jusqu'au tout dernier moment 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'est pas présent dans la base de données, une exception de référence nulle sera générée. C# 6 introduira une autre alternative, l'opérateur de coalescence des propriétés nulles. - donc dans le futur vous pourriez remplacer le dernier morceau par :

var model = groups.FirstOrDefault()?.P;

0voto

Th3B0Y Points 655

Dans le noyau EF 5 et 6 :

var results = await DbSet.Where(t => <your condition>)
                         .Include(t => t.Children.OrderBy(child => child.YourProperty))
                         .ToListAsync(cancellationToken);

-1voto

Juliano Oliveira Points 118

En plus d'avoir besoin de commander, j'avais besoin de limiter les résultats des enfants. Je l'ai fait comme ceci :

var transactions = await _context.Transaction
            .Include(x => x.User)
            .OrderByDescending(x => x.CreatedAt)
            .Where(x => x.User.Id == _tenantInfo.UserId)
            .Take(10)
            .ToListAsync();

var viewmodel = _mapper.Map<UserViewModel>(transactions.First().User);

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