Подтвердить что ты не робот

EF: ленивая загрузка, оживленная загрузка и "перечисление перечислимого"

Я нахожу, что меня смущает ленивая загрузка и т.д.

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

(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, кстати... хотя вопросы могут применяться в более общем плане.)

4b9b3361

Ответ 1

Ваше описание (1) верное, но это пример загружаемой, а не ленивой загрузки.

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

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

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

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

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

Отключение Lazy Loading - это не то же самое, что включение Eager Loading. В этом примере:

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

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

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

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

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

Ответ 2

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

Одна вещь, о которой нужно помнить, состоит в том, что те Includes могут дышать, если вы запрашиваете несколько объектов:

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

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