Paresseux vs désireux de chargement de la performance sur Entity Framework
J'ai donc le modèle suivant les classes sur mon DbContext:
Chaque fois que j'ai rendu une liste de LoanApplication objets-je faire quelque chose comme ceci:
var context = new MyContext();
var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable);
Il renvoie un IQueryable que puis-je convertir à un ViewModel de ce genre sur mon contrôleur de l'appel de la méthode:
var vm = applications.Select(d => new LoanApplicationViewModel(d));
À l'intérieur de la LoanApplicationViewModel
constructeur j'accepte l'entité de l'objet et de faire le correspondant de la cartographie. Le truc, c'est que, depuis que les Avocats de la collection est une de navigation de la propriété, un appel est fait à la base de données chaque fois qu'un nouveau modèle de vue est instanciée. La moyenne du nombre d'avocats par demande est de deux, ce qui veut dire que si je le rendu d'un tableau listant les 10 dernières applications de l'app est de faire environ 18-20 voyages à la base de données.
J'ai pensé qu'il y avait une meilleure façon d'obtenir cette collection, j'ai donc modifié ma requête initiale à avec impatience charge de la collecte de la sorte:
var applications = context.LoanApplications.Include("Solicitors").Where...
Bien que cela réduit le nombre d'appels à la base de données à un seul, la requête a été beaucoup plus lent, environ 50% plus lent.
La base de données est hébergée sur SQL Azure, et nous avons mis en œuvre Transitoire de gestion des Pannes, mais je veux réduire la quantité d'appels à la base de données, sans réduire le temps de réponse de la performance.
Quelle est la meilleure pratique ici?
Vous ne pouvez pas avoir les deux - c'est à propos de faire le meilleur compromis je dirais soit désireux de charge/Include (je serai d'accord que c'est la meilleure si nécessaire) - si vous avez besoin de les avoir dès le début. Ou ne se charge pas du tout - pas désireux de charge. Puis plus tard, ne
Reload
pour une entité, vous devez avoir à jour (c'est à dire que vous n'avez pas l'impact initial) - ou vous pouvez le faire, d'automatiser/intégrer dans quelque chose de plus intelligent. Ce n'est pas idéal, rien n'est - mais c'est ce que vous obtenez.Comme ma réponse ci-dessous, le profil. Peut-être que les avocats ont déjà été chargé dans MyContext avant. (Vous ne sont sûrement pas la création d'un nouveau contexte pour chaque requête, non?) Ou peut-être votre modèle de vue .Sélectionnez omettre db colonnes qui a beaucoup de MO de données?
Aussi, je recommande de ne pas passer IQueryable autour de l'application. Vous n'avez aucun moyen de savoir ce que la Requête a déjà été appliqué lorsque vous le passer autour et il fait des essais difficiles. (Tout est maintenant lié à QueryProvider dans le IQueryable, et la seule façon de vérifier si la requête est traduite correctement est de l'exécuter à l'encontre de SQL. Ensuite, les gens juste de commencer à cogner avec une Liste<T>, et faire un désordre complet, et les analyses, puis ne prouve rien, comme une Requête contre la Liste<T> ne l'est pas forcément traduire correctement en SQL et s'exécute correctement sur SQL.)
OriginalL'auteur amhed | 2013-04-03
Vous devez vous connecter pour publier un commentaire.
"Quelle est la meilleure pratique ici?"
La meilleure pratique consiste à
Maintenant qui peut sembler un peu hors de propos, mais à partir de ce point de vue, le plan de chargement vous PROFILÉ pour être optimal dans votre domaine d'application est la bonne façon de procéder.
Il n'y a pas de "meilleure pratique" d'avides/paresseux. C'est pourquoi les deux options sont disponibles. Aussi, si le tsql est votre goulot de bouteille et de la commutation entre les avides/paresseux n'est toujours pas frapper votre objectif de performance, vous aurez besoin d'aller en bas d'une pléthore d'autres outils, tels que l'analyseur de requêtes et de plan de requête de l'analyseur dans SSMS.
De fond:
J'ai été googler "désireux de chargement lent" et il est venu ici. Voici mon résultat:
Désireux de chargement: 106ms
Chargement différé: 11 ms + 5ms + 5ms
Chargement paresseux gagne à la fin de l'histoire.
Êtes-vous sûr que 106ms n'est pas simplement de l'Entity framework setup pour la première utilisation de la DbContext dans une application?
le profilage est fait dans le réel de l'application, dans ce cas, une application web. Ce n'est pas un de ces "exécuter dans une application de console et de les comparer". La complexité introduite par la durée de vie de la gestion de la politique de la COI conteneurs et le modèle d'utilisation de l'envie/le chargement paresseux à travers les différentes couches d'une application est la raison pour laquelle l'ensemble de "profil".
Je pense que le profil de cas est erroné. Les résultats semblent réalistes, mais le retour sur les ensembles sont différents. D'élaborer, désireux de charge ici renvoie un objet foo (selon sa clé), toutes ses Réponses et toutes ses pièces Jointes. Lazy load (avec en commentaire les lignes) renvoie uniquement l'objet Foo. Tout le reste doit être chargé paresseusement plus tard (sorcière qui semble manquer dans le code). Je pense que vous devriez ajouter deux foreach instructions pour le chargement paresseux, qui serait (get) les réponses et les pièces jointes et de les mesurer en charge paresseux. Que devrait produire les mêmes ensembles de données récupérées, et des temps corrects.
+1 - mesure. J'ai échangé un 3-génération comprennent instruction select avec lazy loading et le temps de traitement est passé de 2:45 à 1:56. À l'aide de EF 6.1.3 avec SQlite avec 229 parents, 18k enfants & 50k petits-enfants.
OriginalL'auteur Sleeper Smith
En plus des instructions SQL qui donne un immense résultats ou des lots d'appels lorsque vous utilisez à la fois désireux et paresseux, il existe un énorme travail qui a lieu par la mise et de cartographie sur l'ObjectContext/DbContext du résultat. Cela entraîne un gain de performance énorme coup et je ne peux pas vraiment recommander ces lors de la récupération de grandes quantités de données.
La meilleure solution est de spécifier explicitement Sélectionnez appeler. Cependant, il est un peu difficile de vous donner un exemple sur la façon de le faire sans savoir comment votre viewmodel objet est construit. Donc, ce que je fais ici est de vous donner un exemple qui utilise anonyme de l'objet en tant que résultat de la requête.
Cet exemple vous donne des contacts avec des informations sur le client, le contact appartient.
Cette requête va générer une instruction SQL SELECT qui relie les Contacts avec la Clientèle à l'aide d'une jointure interne, et ensuite seulement de sélectionner le Contact.Id Contact.Le nom et le Client.Nom des colonnes.
Cette solution est beaucoup plus le moyen le plus efficace pour récupérer des données à partir du serveur si vous n'avez pas l'intention de travailler avec les données et enregistrer les modifications apportées à droite vers le même contexte. Il n'utilisons pas impatient, ni le chargement paresseux.
OriginalL'auteur Rune G
Si vous pouviez en quelque sorte requête vos avocats table et le filtre de la requête à l'aide de votre déjà récupéré la liste des applications, puis extraites des Entités seraient mis en cache dans votre contexte, qui, je crois, sera ensuite utilisé pour la navigation de la propriété au lieu de frapper la base de données.
Je ne suis pas sûr exactement comment écrire les avocats de l'extraction de la requête, mais je pensais à quelque chose comme ceci
peut-être que je n'étais pas clair dans ce que je voulais dire. le DbContext met en cache les entités, donc si vous avez les Avocats dans le cache déjà, lorsque vous appelez la propriété de navigation sur les applications, il les mettra dans le cache au lieu de la base de données. Le code que j'ai proposé est d'optimiser la mise en cache des avocats de sorte que vous êtes seulement de l'extraction de ceux que vous aurez vraiment besoin.
Il a donné un essai, a gagné un peu de vitesse sur la requête, mais le paresseux option de chargement est encore plus rapide. Avez-vous une idée de ce que les meilleures pratiques dans ce scénario?
Si c'est une requête que vous utilisez tout le temps, et les données réelles (colonnes) que vous utilisez à partir de deux entités est petite, vous pourriez envisager d'utiliser des
SqlQuery<CustomClassForReturnedColumns>("Your raw sql query here")
. Ce serait la solution optimale performance sage.Non, en fait, j'ai un modèle de référentiel, et ce même requête est utilisée par de nombreuses autres entités (j'ai simplifié la requête de la question). Faire un sql brut d'appel à l'encontre de l'objectif de l'aide de Entity Framework et les modèles réutilisables code que j'ai mis en place.
OriginalL'auteur Malcolm O'Hare
Avez-vous songé à utiliser en mode sql?
Je n'ai pas tout à fait sûr de Sql Azure. Cependant dans sql server, vous pouvez avoir des performances lors de l'assemblage de 2 tables sans avoir un bon index. Peut-être que cela se produise dans votre requête.
À noter, votre interrogation est l'accès 1 table avec la clause where, 2 appels. Et dans l'après requête, de l'accès aux 2 tables avec clause where, 1 appel. Il est joignez-vous après une requête et est susceptible d'avoir besoin d'indice différent.
Vous pouvez créer une vue sql pour s'assurer qu'un index approprié est utilisé. Ensuite, faites votre demande d'appel de la vue. Procédure stockée peut être utilisée à cette fin aussi, mais il est moins adapté pour cela.
Êtes-vous sûr que l'index est utilisée pendant le traitement? Et avez-vous essayé de faire la même requête (join) en sql directement?
Yes sir! J'ai vérifié
OriginalL'auteur Fendy
Désireux de chargement extrait redondante des données de base. Il va prendre beaucoup de mémoire, si le graphe d'objet dans son contexte ne stocke qu'un seul master de données par entité, mais SQL dump beaucoup de données dans son assiette.
J'ai pris la suite de l'image de ici
Si vous le voyez, les Données de l'Utilisateur table également répété autant que UserDetails table dans le jeu de résultats de la requête SQL. Qui semblent facteur de différenciation de la performance (Dans votre cas, le maître des colonnes a plus d'enregistrements ensuite en détail le tableau).
Si la performance est votre préoccupation principale, je Vous recommande d'utiliser LINQ rejoindre avec la même clause where, tandis que l'extraction de données pour la table détaillée séparément
Donc dans votre cas :-
etape 1
et puis
etape 2
Merci, votre question m'a fait revoir mon propre code 🙂
OriginalL'auteur Sutikshan Dubey