LINQ To Entities и отложенная загрузка

В спорном сообщении в блоге Сегодня хакерская атака рассуждает о том, что, по всей видимости, является ошибкой в ​​новой платформе LINQ To Entities:

Допустим, я ищу покупателя:

var alice = data.Customers.First( c => c.Name == "Alice" );

Хорошо, это прекрасно работает. А теперь посмотрим, найду ли я один из ее заказов:

 var order = ( from o in alice.Orders
          where o.Item == "Item_Name"
          select o ).FirstOrDefault();

LINQ-to-SQL найдет дочернюю строку. LINQ-to-Entities ничего не возвращает.

Теперь предположим, что я перебираю все заказы в базе данных:

foreach( var order in data.Orders ) { 
Console.WriteLine( "Order: " + order.Item ); }

А теперь повтори мой поиск:

var order = ( from o in alice.Orders
          where o.Item == "Item_Name"
          select o ).FirstOrDefault();

Вау! LINQ-to-Entities внезапно сообщает мне, что дочерний объект существует, несмотря на то, что ранее я сказал, что его не было!

Моя первоначальная реакция заключалась в том, что это должна быть ошибка, но после дальнейшего рассмотрения (и поддержанный командой ADO.NET), я понял, что такое поведение было вызвано тем, что Entity Framework не загружала подзапрос «Заказы» с ленивой загрузкой, когда Алиса извлекается из контекста данных.

Это потому, что заказ - это запрос LINQ-To-Object:

var order = ( from o in alice.Orders
      where o.Item == "Item_Name"
      select o ).FirstOrDefault();

И никаким образом не обращается к тексту данных, в то время как его цикл foreach:

 foreach( var order in data.Orders )

Осуществляет доступ к тексту данных.

LINQ-To-SQL фактически создавал ленивые загружаемые свойства для заказов, так что при обращении к ним выполнялся другой запрос, LINQ to Entities оставляет вам возможность вручную извлекать связанные данные.

Я не большой поклонник ORM, и именно в этом причина. Я обнаружил, что для того, чтобы все данные, которые вы хотите, были у вас под рукой, они многократно выполняют запросы за вашей спиной, например, указанный выше запрос linq-to-sql может запускать дополнительный запрос для каждой строки клиентов для получения заказов. .

Однако то, что EF этого не делает, по-видимому, в значительной степени нарушает принцип наименьшего удивления. Хотя это технически правильный способ делать что-то (вы должны запустить второй запрос для получения заказов или получить все из представления), он не ведет себя так, как вы ожидаете от ORM.

Итак, это хороший дизайн фреймворка? Или Microsoft слишком много думает об этом за нас?


person FlySwat    schedule 03.12.2008    source источник


Ответы (6)


Джон,

Я также играл с linq для объектов. Ему предстоит пройти долгий путь, прежде чем он догонит linq to SQL. Мне пришлось использовать linq to entity для материала Table per Type Inheritance. Недавно я нашел хорошую статью, в которой объясняется, как работает одна компания и две разные технологии ORM, здесь < / а>.

Однако вы можете выполнить ленивую загрузку, выполнив следующие действия:

// Lazy Load Orders 
var alice2 = data.Customers.First(c => c.Name == "Alice");

// Should Load the Orders
if (!alice2.Orders.IsLoaded)
    alice2.Orders.Load();

или вы можете просто включить заказы в исходный запрос:

// Include Orders in original query
var alice = data.Customers.Include("Orders").First(c => c.Name == "Alice");

// Should already be loaded
if (!alice.Orders.IsLoaded)
    alice.Orders.Load();

Надеюсь, поможет.

Дэйв

person CraftyFella    schedule 18.12.2008
comment
Я согласен, это сообщение в блоге не полностью информировано. Я считаю, что им следовало потратить немного больше времени на изучение L2E, прежде чем его так сильно критиковать - это как если бы они решили, что им это не нравится, прежде чем написать статью, что вызвало огромный переполох, потому что они не знали, что им нужно загрузить () ... - person naspinski; 06.01.2009

Итак, это хороший дизайн фреймворка? Или Microsoft слишком много думает об этом за нас?

Что ж, давайте проанализируем это - все мысли, которые делает Microsoft, чтобы нам не приходилось делать нас более ленивыми программистами. Но в целом это действительно делает нас более продуктивными (по большей части). Итак, они сверх думают или они просто думают за нас?

person BenAlabaster    schedule 03.12.2008

Если бы LINQ-to-Sql и LINQ-to-Entities поступали от двух разных компаний, это было бы приемлемым различием - нет закона, согласно которому все LINQ-To-Whatevers должны быть реализованы одинаково.

Однако они оба созданы Microsoft - и нам не нужно досконально разбираться в их внутренних группах разработчиков и процессах, чтобы знать, как использовать две разные вещи, которые на первый взгляд выглядят совершенно одинаково.

У ORM есть свое место, и они действительно заполняют пробел для людей, пытающихся довести дело до конца, но пользователи ORM должны точно знать, как их ORM выполняет свои задачи - обращение с ним как с непроницаемым черным ящиком только приведет вас к неприятностям.

person Greg Hurlman    schedule 03.12.2008

Я сочувствую, потеряв несколько дней на решение этой самой проблемы.

«Ошибка», если таковая имеется, состоит в том, что существует разумная тенденция ожидать, что слой абстракции будет изолировать от подобных проблем. Вдвойне переход от LINQ к Entities и к уровню базы данных.

При необходимости переключиться с MS-SQL (с использованием LingToSQL) на MySQL (с использованием LinqToEntities), например, можно было бы подумать, что LINQ, по крайней мере, будет таким же, если не только для экономии затрат на необходимость переписывать программу. логика.

Необходимость засорять код .Load () и / или LINQ с помощью .Include () просто потому, что измененный механизм сохраняемости под капотом кажется немного тревожным, особенно при тихом сбое. Уровень LINQ должен, по крайней мере, вести себя согласованно.

Ряд фреймворков ORM используют прокси-объект для прозрачной динамической загрузки ленивого объекта, вместо того, чтобы просто возвращать значение null, хотя я был бы доволен исключением, если не загружена коллекция.

Я не склонен поверить в отговорку "они сделали это намеренно для вашей выгоды"; другие структуры ORM позволяют вам при необходимости аннотировать, хотите ли вы активную или ленивую загрузку. То же самое можно было сделать и здесь.

person Walt Stoneburner    schedule 26.02.2010

Я мало знаю об ORM, но как пользователь LinqToSql и LinqToEntities я надеюсь, что когда вы попытаетесь запросить заказы для Алисы, он сделает дополнительный запрос для вас, когда вы сделаете запрос linq (в отличие от того, чтобы ничего не запрашивать или запрашивая все для каждой строки).

Кажется естественным ожидать

from o in alice.Orders where o.Item == "Item_Name" select o

для работы, учитывая, что это одна из причин, по которой люди в первую очередь используют ORM (для упрощения доступа к данным).

Чем больше я читаю о LinqToEntities, тем больше я думаю, что LinqToSql адекватно удовлетворяет потребности большинства разработчиков. Обычно мне просто нужно сопоставление таблиц один-к-одному.

person dtc    schedule 03.12.2008

Даже если вам не обязательно знать о внутренних командах и процессах разработки Microsoft, дело в том, что эти две технологии - два совершенно разных зверя.

Конструктивное решение LINQ to SQL для простоты заключалось в неявной отложенной загрузке коллекций. Команда ADO.NET Entity Framework не хотела выполнять запросы без ведома пользователя, поэтому они разработали API для явной загрузки для первого выпуска.

LINQ to SQL был передан группе ADO.NET, поэтому вы можете увидеть консолидацию API-интерфейсов в будущем, или LINQ to SQL будет добавлен в Entity Framework, или вы можете увидеть атрофию LINQ to SQL из-за пренебрежения и в конечном итоге стать устаревшим .

person Mark Cidade    schedule 09.12.2008