Выберите N + 1 в следующем Entity Framework - PullRequest
10 голосов
/ 21 февраля 2011

Одна из немногих действительных жалоб, которые я слышу об EF4 в отношении NHibernate, заключается в том, что EF4 плохо обрабатывает лениво загруженные коллекции.Например, в лениво загруженной коллекции, если я скажу:

if (MyAccount.Orders.Count() > 0) ;

EF потянет всю коллекцию вниз (если это еще не сделано), в то время как NH будет достаточно умен для выдачи select count(*)

У NH также есть неплохая пакетная загрузка, чтобы помочь с проблемой select n + 1.Насколько я понимаю, ближайший EF4 может прийти к этому с помощью метода Include.

Не упустила ли команда EF какие-либо признаки того, что это будет исправлено в следующей итерации?Я знаю, что они усердно работают над POCO, но, похоже, это будет популярным решением.

1 Ответ

13 голосов
/ 21 февраля 2011

То, что вы описываете, не является проблемой N + 1.Пример проблемы N + 1: здесь .N + 1 означает, что вы выполняете N + 1 выбор вместо одного (или двух).В вашем примере это, скорее всего, будет означать:

// Lazy loads all N Orders in single select
foreach(var order in MyAccount.Orders)
{
  // Lazy loads all Items for single order => executed N times
  foreach(var orderItem in order.Items)
  {
     ...
  }
}

Это легко решить с помощью:

// Eager load all Orders and their items in single query
foreach(var order in context.Accounts.Include("Orders.Items").Where(...))
{
 ...
}

Ваш пример выглядит для меня действительным.У вас есть коллекция, которая выставляет IEnumerable, и вы выполняете операцию Count над ней.Коллекция загружается лениво и подсчет выполняется в памяти.Возможность перевода запроса Linq в SQL доступна только в IQueryable с деревьями выражений, представляющими запрос.Но IQueryable представляет запрос = каждый доступ означает новое выполнение в БД, поэтому, например, проверка цикла в цикле будет выполнять запрос БД в каждой итерации.

Так что это больше о реализации динамического прокси.


Подсчет связанных сущностей без их загрузки будет уже возможен в первом коде CTP5 (окончательный выпуск будет называться EF 4.1) при использовании DbContext вместо ObjectContext, но не путем прямого взаимодействия со сбором.Вам нужно будет использовать что-то вроде:

int count = context.Entry(myAccount).Collection(a => a.Orders).Query().Count();

Query метод возвращает подготовленный IQueryable, который, вероятно, и запускает EF, если вы используете отложенную загрузку, но можете дополнительно изменить запрос - здесь я использовал Count.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...