EF требует вечно, чтобы сгенерировать этот запрос

У меня есть отношения родитель-дочерняя таблица. В репозитории я делаю это:

return (from p in _ctx.Parents  
.Include( "Children" )  
select p).AsQueryable<Parent>();  

Затем в фильтре, Я хочу отфильтровать родителя по списку дочерних идентификаторов:

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered =
from p in qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) ) select s;  

Мой список идентификаторов огромен. Это генерирует простой оператор SQL, который имеет огромный список идентификаторов «in (1,2,3 ...)», но сам по себе не требует значительного времени для запуска. EF, однако, занимает около минуты, чтобы сгенерировать утверждение. Я доказал это, установив точку останова и вызвав:

((ObjectQuery<Parent>)filtered).ToTraceString();

Это занимает все время. Проблема в моем последнем утверждении linq? Я не знаю другого способа сделать эквивалент Child.ChildId в (ids). И даже если мое утверждение linq плохое, как в мире это может занять так много времени?

10
задан bdukes 17 August 2010 в 15:50
поделиться

2 ответа

Перепишите ваш запрос в синтаксисе Lambda, и это сократит время на целых 3 секунды (по крайней мере, так было в моем проекте EF).

return _ctx.Parents.Include( "Children" ).AsQueryable<Parent>();  

и

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered = qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) );
2
ответ дан 4 December 2019 в 03:15
поделиться

К сожалению, создание запросов в Linq to Entities - довольно тяжелый удар, но я обнаружил, что это обычно экономит время благодаря возможности создавать запросы из их компонентов до того, как фактически попадут в базу данных.

Вполне вероятно, что способ реализации метода Contains использует алгоритм, предполагающий, что Contains обычно используется для относительно небольшого набора данных. Согласно моим тестам, количество времени, которое требуется для каждого идентификатора в списке, начинает стремительно расти примерно до 8000.

Так что это может помочь разбить ваш запрос на части. Сгруппируйте их в группы по 1000 или меньше и объедините кучу выражений Where .

var idGroups = ids.GroupBy(i => i / 1000);
var q = Parents.Include("Children").AsQueryable();
var newQ = idGroups.Aggregate(q, 
    (s, g) => s.Concat(
                  q.Where(w => w.Children.Any(wi => g.Contains(wi.ChildId)))));

Это значительно ускоряет процесс, но этого может быть недостаточно для ваших целей, и в этом случае вам придется прибегнуть к хранимой процедуре. К сожалению, этот конкретный вариант использования просто не вписывается в «коробку» ожидаемого поведения Entity Framework. Если бы ваш список идентификаторов мог начинаться как запрос из того же контекста сущности, Entity Framework сработала бы нормально.

4
ответ дан 4 December 2019 в 03:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: