web-dev-qa-db-fra.com

Performances .Include () vs .Load () dans EntityFramework

Lorsque vous interrogez une grande table où vous devez accéder aux propriétés de navigation plus tard dans le code (je ne veux pas explicitement utiliser le chargement paresseux), qu'est-ce qui fonctionnera mieux .Include() ou .Load()? Ou pourquoi utiliser l'un sur l'autre?

Dans cet exemple, toutes les tables incluses ne contiennent qu'environ 10 entrées et les employés, environ 200, et il est possible que la plupart d'entre elles soient chargées de toute façon avec include car elles correspondent à la clause where.

Context.Measurements.Include(m => m.Product)
                    .Include(m => m.ProductVersion)
                    .Include(m => m.Line)
                    .Include(m => m.MeasureEmployee)
                    .Include(m => m.MeasurementType)
                    .Where(m => m.MeasurementTime >= DateTime.Now.AddDays(-1))
                    .ToList();

ou

Context.Products.Load();
Context.ProductVersions.Load();
Context.Lines.Load();
Context.Employees.Load();
Context.MeasurementType.Load();

Context.Measurements.Where(m => m.MeasurementTime >= DateTime.Now.AddDays(-1))
                    .ToList();
61
Staeff

Cela dépend, essayez les deux

Lorsque vous utilisez Include(), vous obtenez le avantage de charger toutes vos données en un seul appel vers le magasin de données sous-jacent. S'il s'agit d'un serveur SQL distant, par exemple, cela peut représenter une amélioration majeure des performances.

Le inconvénient est que Include() les requêtes ont tendance à obtenir vraiment compliqué, surtout si vous avez des filtres (Where() appels, par exemple) ou essayez de grouper. EF générera des requêtes très fortement imbriquées en utilisant les instructions sub -SELECT et APPLY pour obtenir les données souhaitées. Il est également beaucoup moins efficace: vous récupérez une seule ligne de données avec toutes les colonnes d'objet enfant possibles. Ainsi, les données de vos objets de niveau supérieur sont répétées de nombreuses fois. (Par exemple, un objet parent unique avec 10 enfants produira 10 lignes, chacune contenant les mêmes données que les colonnes de l'objet parent.) J'ai eu single les requêtes EF sont si complexes qu'elles ont provoqué des blocages quand fonctionnant en même temps que la logique de mise à jour EF.

La méthode Load() est beaucoup plus simple. Chaque requête est une instruction simple, simple et directe SELECT sur une seule table. Celles-ci sont beaucoup plus faciles de toutes les manières possibles , sauf que vous devez en effectuer plusieurs (éventuellement plusieurs fois plus). Si vous avez des collections de collections imbriquées, vous devrez peut-être même parcourir en boucle vos objets de niveau supérieur et Load leurs sous-objets. Cela peut devenir incontrôlable.

Règle rapide

Essayez de éviter d'avoir n'importe quel plus de trois appels Include dans une même requête. Je trouve que les requêtes de EF deviennent trop laides pour être reconnues au-delà de cela; cela correspond également à ma règle générale pour les requêtes SQL Server, à savoir que jusqu'à quatre instructions JOIN dans une requête fonctionnent très bien, mais il est temps ensuite d'envisager le refactoring .

Cependant, tout cela n’est qu’un point de départ.

Cela dépend de votre schéma, de votre environnement, de vos données, et de nombreux autres facteurs.

À la fin, vous aurez juste besoin de essayez-le dans un sens.

Choisissez un modèle raisonnable "par défaut" à utiliser, vérifiez s'il est suffisant, et si non, optimisez-le selon vos goûts.

81
Michael Edenfield

Include() sera écrit dans SQL sous la forme JOIN: un aller-retour à la base de données.

Chaque instruction Load()- "charge explicitement" les entités demandées, donc un aller-retour à la base de données par appel.

Ainsi, Include() sera probablement le choix le plus judicieux dans ce cas, mais cela dépend de la structure de la base de données, de la fréquence à laquelle ce code est appelé et de la durée de vie de votre DbContext. Pourquoi n'essayez-vous pas dans les deux sens, profilez les requêtes et comparez les horaires?

Voir Chargement des entités liées .

20
CodeCaster

Je suis d'accord avec @MichaelEdenfield dans son réponse mais je voulais commenter le scénario des collections imbriquées. Vous pouvez éviter de faire des boucles imbriquées (et les nombreux appels résultants à la base de données) en retournant la requête.

Plutôt que de parcourir en boucle la collection Orders du client, puis d'effectuer une autre boucle imbriquée dans la collection OrderItems de Order, vous pouvez interroger les OrderItems directement avec un filtre tel que le suivant.

context.OrderItems.Where(x => x.Order.CustomerId == customerId);

Vous obtiendrez les mêmes données résultantes que les charges dans les boucles imbriquées, mais avec un seul appel à la base de données.

En outre, il y a un cas spécial qui devrait être considéré avec includes. Si la relation entre le parent et l'enfant est un à un, le problème des données parent renvoyées plusieurs fois ne le sera pas.

Je ne sais pas quel serait l'effet si le cas majoritaire était celui où aucun enfant n'existait - beaucoup de valeurs nulles? Les enfants clairsemés dans une relation un à un conviendraient peut-être mieux à la technique d'interrogation directe que j'ai décrite ci-dessus.

10
Scott Munro

Include est un exemple de chargement rapide, dans lequel vous ne chargez pas seulement les entités que vous interrogez, mais également toutes les entités associées.

Load est une substitution manuelle du EnableLazyLoading. Si celui-ci est défini sur false. Vous pouvez toujours charger paresseusement l'entité que vous avez demandée avec .Load()

5
Henk Jansen

Une dernière chose à ajouter à ce fil. Cela dépend du serveur que vous utilisez. Si vous travaillez sur un serveur SQL, vous pouvez utiliser le chargement rapide, mais pour SQLite, vous devez utiliser .Load () pour éviter les exceptions de chargement croisé, car SQLite ne peut pas traiter certaines instructions include qui dépassent un niveau de dépendance.

1
PernerOl

Il est toujours difficile de décider si on doit utiliser Eager, Explicit ou même Lazy Loading.
Ce que je recommanderais de toute façon, c’est toujours de faire du profilage. C'est la seule façon de vous assurer que votre demande sera performante ou non.
Il existe de nombreux outils qui vous aideront. Jetez un oeil à cet article de Julie Lerman où elle énumère différentes façons de faire du profilage. Une solution simple consiste à démarrer profilage dans votre SQL Server Management Studio .
N'hésitez pas à parler à un administrateur de base de données (si vous en avez près de chez vous) qui vous aidera à comprendre le plan d'exécution.
Vous pouvez également consulter cette présentation où j'ai écrit une section sur le chargement des données et des performances.

1
MaxSC