EF: отложенная загрузка, полная загрузка и «перечисление перечислимых» - PullRequest
19 голосов
/ 17 сентября 2010

Я нахожу, что запутался в ленивой загрузке и т. Д.

Во-первых, эти два утверждения эквивалентны:

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

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

Другими словами, в (1) "Включает"вызвать загрузку коллекций / свойств навигации вместе с конкретной запрашиваемой коллекцией, независимо от того, используется ли ленивая загрузка ... верно?

И в (2) оператор загрузит всю навигациюсущности, даже если вы специально не запрашиваете их, потому что вы используете готовую загрузку ... верно?

Второе: даже если вы используете загруженную загрузку, данные не будут фактически загружаться из базы данных, пока вы не "перечислить перечислимое ", как в следующем коде:

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

Данные фактически не будут загружены (" перечислимые ") до цикла foreach ... верно?Другими словами, строка «var date» определяет запрос, но запрос не выполняется до цикла foreach.

Учитывая, что (если мои предположения верны), какова реальная разница между энергичной загрузкой и ленивымзагрузка ??Кажется, что в любом случае данные не появляются до перечисления.Я что-то упустил?

(Кстати, мой конкретный опыт связан с разработкой кода, сначала POCO ... хотя вопросы могут применяться в более общем плане.)

Ответы [ 2 ]

17 голосов
/ 17 сентября 2010

Ваше описание (1) является верным, но оно является примером «Стремительной загрузки», а не «отложенной загрузки».

Ваше описание (2) неверно.(2) технически вообще не использует загрузку, но будет использовать отложенную загрузку, если вы попытаетесь получить доступ к каким-либо нескалярным значениям на ваших FlaggedDates.

В любом случае вы правы, что никакие данные не будут загружены изваше хранилище данных, пока вы не попытаетесь «сделать что-то» с _flaggedDates.Однако в каждом случае происходит по-разному.

(1): Стремительная загрузка: как только вы начнете цикл for, каждый из указанных вами объектов будет извлечен из базы данных ивстроенный в гигантскую структуру данных в памяти.Это будет очень дорогая операция, которая потребует огромное количество данных из вашей базы данных.Однако все это произойдет за один прием в одну и ту же базу данных, и будет выполнен один запрос SQL.

(2): отложенная загрузка: когда начинается цикл for, он загружает только объекты FlaggedDates.Однако, если вы обращаетесь к связанным объектам внутри цикла for, эти объекты еще не будут загружены в память.Первая попытка получить запланированные школы для заданной даты FlaggedDate приведет либо к отправке туда и обратно новой базы данных, либо к исключению, потому что ваш контекст уже удален.Поскольку вы будете получать доступ к коллекции scheduleSchools в цикле for, у вас будет новая база данных в оба конца для каждой FlaggedDate, которую вы изначально загрузили в начале цикла for.

Ответ на комментарии

Отключение отложенной загрузки - это не то же самое, что включение активной загрузки.В этом примере:

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

Переменная schools будет содержать пустую коллекцию EntityCollection, потому что я не Include их в исходном запросе (FlaggedDates.First ()), и я отключил отложенную загрузкучтобы они не могли быть загружены после выполнения исходного запроса.

Вы правы, что where d.dateID == 2 будет означать, что будут извлечены только объекты, связанные с этим конкретным объектом FlaggedDate. Однако, в зависимости отна том, сколько объектов связано с этим FlaggedDate, вы все равно можете получить много данных, передаваемых по этому проводу.Это связано с тем, как EntityFramework создает свой SQL-запрос.Результаты SQL-запросов всегда представлены в табличном формате, то есть для каждой строки должно быть одинаковое количество столбцов.Для каждого запланированного объекта должна быть как минимум одна строка в наборе результатов, и, поскольку каждая строка должна содержать не менее некоторое значение для каждого столбца, вы получите каждое скалярное значение в вашем объекте FlaggedDateповторяетсяПоэтому, если у вас есть 10 запланированных школ и 10 интервью, связанных с вашей FlaggedDate, у вас будет 20 строк, каждая из которых содержит каждое скалярное значение в FlaggedDate.Половина строк будет иметь значения NULL для всех столбцов ScheduledSchool, а другая половина будет иметь значения NULL для всех столбцов Interviews.

Однако в этом случае все становится очень плохо, если вы углубляетесь.в данных, которые вы включаете.Например, если у каждого ScheduledSchool есть свойство students, которое вы также включили, то вдруг у вас будет строка для каждого ученика в каждом ScheduledSchool, и в каждой из этих строк будет включено каждое скалярное значение для ScheduledSchool студента(даже если в конечном итоге используются только значения первой строки), вместе с каждым скалярным значением исходного объекта FlaggedDate.Это может сложиться быстро.

Трудно объяснить в письменной форме, но если вы посмотрите на фактические данные, возвращающиеся из запроса с несколькими Include с, вы увидите, что есть много дублирующих данных,Вы можете использовать LinqPad для просмотра SQL-запросов, сгенерированных вашим EF-кодом.

0 голосов
/ 17 сентября 2010

Без разницы. Это не было правдой в EF 1.0, который не поддерживал активную загрузку (по крайней мере, не автоматически). В версии 1.0 необходимо было либо изменить свойство для автоматической загрузки, либо вызвать метод Load () для ссылки на свойство.

Следует иметь в виду, что эти Включения могут затухать, если вы выполняете запросы к нескольким объектам, например так:

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

ObjectDate.MyObjectProperty не будет загружаться автоматически.

...