19

Je trouve que je suis confus au sujet de chargement paresseux, etc.EF: Lazy loading, le chargement impatient, et "le dénombrable énumérant"

En premier lieu, sont ces deux déclarations équivalentes:

(1) Lazy loading: 
_flaggedDates = context.FlaggedDates.Include("scheduledSchools") 
.Include ("interviews").Include("partialDayAvailableBlocks") 
.Include("visit").Include("events"); 

(2) Eager loading: 
_flaggedDates = context.FlaggedDates; 

Dans d'autres mots, dans (1) les "includes" provoquent le chargement des collections de navigation/propriétés avec la collection spécifique demandée, indépendamment du fait que vous utilisez le chargement paresseux ... non?

Et dans (2), l'instruction va charger toutes les entités de navigation même si vous ne les avez pas spécifiquement demandées, parce que vous utilisez un chargement ardent ... non?

Deuxièmement: même si vous utilisez le chargement impatient, les données ne seront pas réellement être téléchargé à partir de la base de données jusqu'à ce que vous « énumèrent les dénombrable », comme dans le code suivant:

var dates = from d in _flaggedDates 
      where d.dateID = 2 
      select d; 
foreach (FlaggedDate date in dates) 
{ 
... etc. 
} 

Les données ne seront pas réellement être téléchargé ("énuméré") jusqu'à la boucle foreach ... non? En d'autres termes, la ligne "var dates" définit la requête, mais la requête n'est pas exécutée avant la boucle foreach. Étant donné que (si mes hypothèses sont correctes), quelle est la vraie différence entre le chargement impatient et le chargement paresseux? Il semble que dans les deux cas, les données n'apparaissent pas avant l'énumération. Est-ce que je manque quelque chose?

(Mon expérience spécifique est avec code en premier lieu, le développement POCO, en passant ... bien que les questions peuvent appliquer de façon plus générale.)

+0

EntityFramework utilise un chargement impatient/paresseux (terme générique utilisé pour le chargement des données) pour extraire les objets et collections associés. Deffered Query Execution est un artefact de LINQ qui permet aux requêtes d'être plus flexibles (elles peuvent ne pas être exécutées du tout dans certains scénarios). – jwize

Répondre

17

Votre description (1) est correct, mais il est un exemple de chargement rapide plutôt que de chargement paresseux.

Votre description de (2) est incorrecte. (2) n'utilise techniquement aucune charge, mais utilisera Lazy Loading si vous essayez d'accéder à des valeurs non-scalaires sur vos FlaggedDates.

Dans les deux cas, vous avez raison qu'aucune donnée ne sera chargée à partir de votre magasin de données tant que vous n'essayez pas de "faire quelque chose" avec les _flaggedDates. Cependant, ce qui se passe est différent dans chaque cas.

(1): Chargement rapide: dès que vous commencez votre boucle for, chacun des objets que vous avez spécifiés sera extrait de la base de données et intégré dans une gigantesque structure de données en mémoire. Ce sera une opération très coûteuse, tirant une énorme quantité de données de votre base de données. Cependant, tout se passe dans un seul aller-retour de base de données, avec une seule requête SQL exécutée.

(2): Chargement paresseux: Lorsque votre boucle for commence, elle ne chargera que les objets FlaggedDates. Cependant, si vous accédez à des objets connexes dans votre boucle for, ces objets ne seront pas encore chargés dans la mémoire. La première tentative d'extraction des scheduledSchools pour un FlaggedDate donné aboutira soit à un nouveau roundtrip de base de données pour récupérer les écoles, soit une exception est levée car votre contexte a déjà été éliminé. Puisque vous seriez en train d'accéder à la collection scheduledSchools à l'intérieur d'une boucle for, vous auriez un nouveau round round trip pour chaque FlaggedDate que vous avez initialement chargé au début de la boucle for.

Reponse aux commentaires

Chargement Lazy est Désactiver pas la même que l'activation Désireuse Chargement.Dans cet exemple:

context.ContextOptions.LazyLoadingEnabled = false; 
var schools = context.FlaggedDates.First().scheduledSchools; 

La variable schools contiendra un EntityCollection vide, parce que je ne les ai pas Include dans la requête initiale (FlaggedDates.First()), et je désactivé le chargement paresseux afin qu'ils ne pouvaient pas être chargé après l'exécution de la requête initiale.

Vous avez raison de dire que le where d.dateID == 2 signifierait que seuls les objets liés à cet objet FlaggedDate spécifique seraient extraits. Cependant, en fonction du nombre d'objets liés à cette FlaggedDate, vous pourriez vous retrouver avec beaucoup de données passer par-dessus ce fil. Cela est dû à la façon dont EntityFramework construit sa requête SQL. Les résultats de requête SQL sont toujours dans un format tabulaire, ce qui signifie que vous devez avoir le même nombre de colonnes pour chaque ligne. Pour chaque objet scheduledSchool, il doit y avoir au moins une ligne dans le jeu de résultats, et comme chaque ligne doit contenir au moins une valeur pour chaque colonne, chaque valeur scalaire de votre objet FlaggedDate se répète. Donc, si vous avez 10 scheduledSchools et 10 interviews associés à votre FlaggedDate, vous aurez 20 lignes contenant chacune une valeur scalaire sur FlaggedDate. La moitié des lignes aura des valeurs nulles pour toutes les colonnes ScheduledSchool, et l'autre moitié aura des valeurs nulles pour toutes les colonnes Interviews.

Lorsque cela devient vraiment mauvais, cependant, c'est si vous allez "profond" dans les données que vous incluez. Par exemple, si chaque ScheduledSchool avait une propriété students, que vous incluiez également, alors vous auriez soudainement une ligne pour chaque Student dans chaque ScheduledSchool, et sur chacune de ces lignes, chaque valeur scalaire pour l'ScheduledSchool de Student serait incluse (même mais seulement les valeurs de la première ligne finissent par être utilisées), ainsi que toutes les valeurs scalaires de l'objet FlaggedDate original. Ça peut s'ajouter rapidement.

Il est difficile d'expliquer par écrit, mais si vous regardez les données réelles revenant d'une requête avec plusieurs Include s, vous verrez qu'il y a beaucoup de données en double. Vous pouvez utiliser LinqPad pour voir les requêtes SQL générées par votre code EF.

+0

Pour (1) (MY (1), je voulais dire que Lazy Loading a été activé.La déclaration est un exemple de chargement impatient à cause de l'inclusion .. droite? – Cynthia

+0

For (2) (MY (2)) ... Si Chargement paresseux est désactivé (en d'autres termes, le chargement impatient est en vigueur), vous dites que les propriétés de navigation ne seront pas chargées avec FlaggedDates? Alors qu'est-ce que le chargement impatient signifie – Cynthia

+0

Réponse à vos modifications: OK, je vois ce que vous dites Mais pour votre point # 1: tous les objets seront tirés, mais seulement ceux spécifiés dans votre requête, par exemple, dans mon exemple j'ai dit d.dateID == 2, donc seulement les objets pour l'objet FlaggedDate avec dateID = 2 aurait les données tirées. Droit? Donc, il ne serait pas si cher tant que vous avez limité la portée de votre requête – Cynthia

0

Aucune différence. Ce n'était pas vrai dans EF 1.0, qui ne supportait pas le chargement hâtif (du moins pas automatiquement). Dans 1.0, vous deviez modifier la propriété à charger automatiquement ou appeler la méthode Load() sur la référence de propriété.

Une chose à garder à l'esprit est que les includes peuvent aller en fumée si vous interrogez sur plusieurs objets comme ceci:

from d in ctx.ObjectDates.Include("MyObjectProperty") 
from da in d.Days 

ObjectDate.MyObjectProperty ne sera pas automatiquement chargé.

Questions connexes