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

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

Dans cet exemple, les tables incluses ne contiennent qu’environ 10 entrées et les employés environ 200 entrées, et il peut arriver que la plupart d’entre elles soient chargées 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(); 

La réponse est “ça dépend, essayez les deux”.

Lorsque vous utilisez Include() , vous bénéficiez du chargement de toutes vos données dans un seul appel au magasin de données sous-jacent. S’il s’agit d’un serveur SQL distant, par exemple, cela peut augmenter considérablement les performances.

L’inconvénient est que les requêtes Include() ont tendance à être vraiment compliquées, surtout si vous avez des filtres (les appels Where() , par exemple) ou essayez de faire un regroupement. EF va générer des requêtes très nestedes en utilisant des 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 chaque colonne d’object enfant possible, de sorte que les données de vos objects de haut niveau seront répétées de nombreuses fois. (Par exemple, un seul object parent avec 10 enfants produira 10 lignes, chacune avec les mêmes données pour les colonnes de l’object parent.) J’ai eu des requêtes EF uniques qui ont provoqué des blocages lors de l’exécution en même temps que EF mettre à jour la logique.

La méthode Load() est beaucoup plus simple. Chaque requête est une instruction SELECT simple, simple et directe sur une seule table. Celles-ci sont beaucoup plus faciles à tous les égards, sauf que vous devez en faire beaucoup (peut-être plusieurs fois plus). Si vous avez des collections nestedes de collections, vous devrez peut-être parcourir vos objects de niveau supérieur et Load leurs sous-objects. Cela peut devenir incontrôlable.

En règle générale, j’essaie d’éviter plus de trois appels Include dans une même requête. Je trouve que les requêtes d’EF deviennent laides à reconnaître au-delà de cela; il correspond également à mes règles de base pour les requêtes SQL Server, à savoir que jusqu’à quatre instructions JOIN dans une même requête fonctionnent très bien, mais qu’il est ensuite temps d’envisager une refactorisation.

Cependant, tout cela n’est qu’un sharepoint départ. Cela dépend de votre schéma, de votre environnement, de vos données et de nombreux autres facteurs. En fin de compte, vous aurez juste besoin de l’essayer dans tous les sens. Choisissez un modèle “par défaut” raisonnable à utiliser, voyez si c’est assez bon et, sinon, optimisez le goût.

Include() sera écrit dans SQL en tant que JOIN : un aller-retour de firebase database.

Chaque Load() “explicitement paresseux” les entités demandées, de sorte qu’une firebase database complète par appel.

Ainsi, Include() sera probablement le choix le plus judicieux dans ce cas, mais cela dépend de la disposition de la firebase database, de la fréquence à laquelle ce code est appelé et de la durée de vie de DbContext . Pourquoi n’essayez-vous pas les deux manières, profilez les requêtes et comparez-les?

Voir Chargement des entités associées .

Je suis d’accord avec @MichaelEdenfield dans sa réponse mais je voulais commenter le scénario des collections nestedes. Vous pouvez contourner les boucles nestedes (et les nombreux appels résultants vers la firebase database) en retournant la requête.

Plutôt que d’effectuer une boucle dans la collection Commandes d’un client, puis effectuer une autre boucle nestede dans la collection OrderItems de Order, vous pouvez interroger les articles de commande directement avec un filtre tel que celui-ci.

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

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

En outre, il existe un cas particulier qui devrait être considéré avec Inclut. Si la relation entre le parent et l’enfant est de un à un, le problème avec les données parent renvoyées plusieurs fois ne serait pas un problème.

Je ne suis pas sûr de l’effet que cela aurait si le cas majoritaire était celui où il n’existait pas d’enfant – beaucoup de nulls? Les enfants rares dans une relation individuelle pourraient mieux convenir à la technique de requête directe que j’ai décrite ci-dessus.

Include est un exemple de chargement rapide, où vous chargez non seulement les entités que vous interrogez, mais également toutes les entités associées.

Load est un remplacement manuel de EnableLazyLoading . Si celle-ci est définie sur false . Vous pouvez toujours charger paresseusement l’entité demandée avec .Load()

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

Encore une chose à append à ce fil. Cela dépend de quel serveur vous utilisez. Si vous travaillez sur un serveur SQL, vous pouvez utiliser le chargement avec impatience, mais pour sqlite, vous devrez utiliser .Load () pour éviter les exceptions de chargement croisé car sqlite ne peut pas traiter certaines instructions d’inclusion plus profondes qu’un niveau de dépendance.