EF берет навсегда, чтобы сгенерировать этот запрос - PullRequest
10 голосов
/ 17 августа 2010

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

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 неверно, как в мире это должно продолжаться?

Ответы [ 2 ]

4 голосов
/ 24 августа 2010

К сожалению, создание запросов в 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 будет работать нормально.

2 голосов
/ 24 августа 2010

Перепишите ваш запрос в синтаксисе 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)) );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...