EF Où (x => x.ColumnVal == 1) vs FirstOrDefault (x => x.Column == 1)

entity-framework linq-to-entities

Question

J'ai eu une requête LINQ qui charge une hiérarchie d'objets comme suit.

Requête n ° 1

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .FirstOrDefault(x => x.Customer.CustomerId == 1 &&
                                    x.OrderId == orderId);

J'avais de gros problèmes de performances avec.
L'utilisation du processeur était proche de 100% et l'utilisation de la mémoire était très élevée.

Et je l'ai modifié comme suit et le problème de performance a été corrigé.

Requête n ° 2

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .Where(x => x.Customer.CustomerId == 1 &&
                           x.OrderId == orderId)
               .FirstOrDefault();



Je veux juste confirmer mes soupçons.
La requête n ° 1 parcourt probablement tous mes enregistrements en mémoire à la recherche d'un enregistrement correspondant.
contre
La requête n ° 2 filtre les enregistrements de la base de données et obtient uniquement le premier enregistrement.

Est-ce pour cela que la requête n ° 1 a des problèmes de performances?

Juste pour être sûr, dois-je utiliser le .Select(x => x) avant le .FirstOrDefault() ?

Requête n ° 3

var result = db.Orders
               .Include("Customer")
               // many other .Include() here
               .Where(x => x.Customer.CustomerId == 1 &&
                           x.OrderId == orderId)
               .Select(x => x)
               .FirstOrDefault();

Réponse acceptée

Non, ils devraient tous deux donner lieu à une même requête SQL lors de l'exécution. Vous pouvez le prouver en consultant SQL Profiler et voir quel est le code SQL exact soumis par EF dans les deux cas. Votre optimisation des performances aurait dû être causée par d'autres facteurs. Voici pourquoi:

La méthode include renvoie un ObjectQuery <T> :

public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>, 
                              IQueryable<T>, IEnumerable<T>, 
                              IOrderedQueryable, IQueryable, 
                              IEnumerable, IListSource

Ce qui signifie que sa méthode FirstOrDefault est livrée avec 2 surcharges:

// Defined by Enumerable:
FirstOrDefault(Func<T, Boolean>)

// Defined by Queryable:
FirstOrDefault(Expression<Func<T, Boolean>>)

Lorsque vous .FirstOrDefault(x => x.Customer.CustomerId == 1 compilateur entrera dans un processus appelé Résolution de surcharge pour déduire le type de l'expression lambda x => x.Customer.CustomerId == 1 puisqu'il est convertible en le type des deux types de paramètres de surcharge.
Le compilateur utilisera un algorithme (que j’essaie encore de trouver dans la spécification du langage C #!), Sachant que la conversion du lambda en Expression<Func<T, Boolean> est une meilleure conversion que celle en Func<T, Boolean> . la surcharge IQueryable .
Par conséquent, vous verrez le prédicat dans le SQL généré lors de son observation dans le profileur SQL.


Réponse populaire

Je pense que le mieux serait d'utiliser ... Où ( condition ) .Take (1) .FirstOrDefault () car Take (1) peut être facilement traduit en SQL sous forme de clause TOP. Quelqu'un avec moi?



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow