EF Core Include преобразуется в SQL с подзапросами, которые не имеют фильтров

#c# #linq #entity-framework-core

Вопрос:

 Meetings
  .Include(a => a.Document)
  .Include(a => a.Plan)
  .Include(a => a.User)
  .Include(a => a.Topics).ThenInclude(e => e.Extra)
  .Include(a => a.Components).ThenInclude(g => g.Extra)
  .Include(a => a.Recipients).ThenInclude(i => i.Info)
  .Single(a => a.MeetingId == 1)
 

Приведенный выше LINQ преобразуется в SQLs с подзапросами, не имеющими фильтров:

 SELECT [blah]
FROM (
    SELECT TOP(2) [blah]
    FROM [Meeting] AS [m]
    LEFT JOIN [Plan] AS [s] ON [m].[PlanId] = [s].[PlanId]
    LEFT JOIN [User] AS [u] ON [m].[UserId] = [u].[Id]
    WHERE [m].[MeetingId] = 1
) AS [t]
LEFT JOIN [Document] AS [m0] ON [t].[MeetingId] = [m0].[MeetingId]
LEFT JOIN (
    SELECT [blah]
    FROM [Topic] AS [m1]
    LEFT JOIN [Extra] AS [a] ON [m1].[ExtraId] = [a].[ExtraId]
) AS [t0] ON [t].[MeetingId] = [t0].[MeetingId]
LEFT JOIN (
    SELECT [blah]
    FROM [Component] AS [m2]
    LEFT JOIN [Extra] AS [a0] ON [m2].[ExtraId] = [a0].[ExtraId]
) AS [t1] ON [t].[MeetingId] = [t1].[MeetingId]
LEFT JOIN (
    SELECT [blah]
    FROM [Recipient] AS [m3]
    INNER JOIN [Info] AS [l] ON [m3].[InfoId] = [l].[InfoId]
) AS [t2] ON [t].[MeetingId] = [t2].[MeetingId]
 

Если вы посмотрите на последние 3 соединения, там нет фильтров, поэтому они будут сканировать всю таблицу.

Есть ли способ это исправить?

Кстати, это EF Core 3.1.

Комментарии:

1. Какие фильтры вы ожидаете? Сам JOIN по себе является фильтром, и в этом случае больше никаких фильтров не требуется. Поле соединения-это FK, которое обычно индексируется, поэтому не будет выполняться полное сканирование таблицы, а будет выполняться типичное сканирование диапазона индексов.

2. Хотя вы действительно смотрели план выполнения запроса? Способ написания запроса и способ его выполнения редко совпадают, если только это не какой-то очень простой запрос. Последние 3 подзапроса имеют неявные фильтры, потому что они являются частью объединения; sqlserver не будет наивно запускать объединение, генерировать несколько миллионов записей, а затем сканировать их в поисках идентификатора; он знает, что их результаты будут объединены по идентификатору, и идентификатор выбран, поэтому требование к идентификатору может быть введено во внутренний запрос в качестве фильтра. Невероятно трудно победить этот механизм, даже иногда, когда вы этого хотите..

3. Есть ли способ это исправить? — во-первых, убедитесь абсолютно, что это проблема. Ничто из опубликованного до сих пор не доказывает, что это так..

4. @CaiusJard На самом деле это проблема. У LINQ есть тайм-аут для этого SQL. Я не уверен, как SQL Server справляется с этим или планом выполнения (я не разбираюсь в БД). Все, что я знаю, это: 1. время ожидания истекло 2. он возвращает мне более 7000 строк, которые в основном являются дубликатами.

5. @IvanStoev Итак , скажем, 2-й подзапрос, который присоединяется Topic Extra , я ожидаю where , что он фильтрует Topic MeetingId значение.