Linq2Entities CompiledQuery для запроса, который использует соединения

У меня есть запрос, который не работает слишком хорошо, например, сгенерированный код SQL является неоптимальным.

Исходное утверждение выглядело примерно так (упрощенно):

ctx.Table1.Where(t => ...)
          .OrderBy(t => ....)
          .Select(t => new {Table1 = t,
                            SomeProperty = t.Table2.SomeProperty,
                            SomeProperty2 = t.Table2.SomeProperty2,
                            AnotherProperty = t.Table3.AnotherProperty,
                            ...
                            }

Я заглянул в SQL Profiler и обнаружил, что сгенерированный SQL будет соединяться с одной и той же таблицей несколько раз, и выполнение инструкции займет около 1 секунды.

Затем я переписал утверждение к чему-то вроде этого:

from t in ctx.Table1
join t2 in ctx.Table2 on t.key equals t2.key into lt2
from t2 in lt2.DefaultIfEmpty()
join t3 in ctx.Table3 on t.key equals t3.key into lt3
from t3 in lt3.DefaultIfEmpty()
where t ...
orderby t...
select new {Table1 = t, .... }

Это привело к гораздо более приятному утверждению, что при извлечении из SQL-профилировщика и выполнении в Management studio он вдвое быстрее, чем оператор, сгенерированный кодом в предыдущем примере.

Однако при выполнении кода из второго примера время, затрачиваемое EF на генерацию выражения, намного превосходит время, полученное при оптимизации запроса.

Итак, как мне написать заявление номер два в качестве CompiledQuery, Я в основном не знаю, как вернуть анонимный тип из CompiledQuery,

2 ответа

Ты можешь использовать Tuple класс, если ваш возвращаемый объект будет иметь 8 или менее свойств. Если у вас есть больше свойств и вы не хотите объявлять класс для этих свойств, вы можете использовать dynamic как тип возвращаемого значения.

Обходной путь, который я нашел для использования CompiledQueries:

  1. Добавьте закрытый метод InitQueryX() перед каждым методом QueryX (), который использует LINQ to Entity.
  2. Используйте атрибуты и отражение, чтобы вызвать все методы InitQueryX() из метода Init ().
  3. Вызовите метод Init () один раз при запуске приложения.

Это заставляет компилировать запросы при запуске, но позволяет писать запросы более гибко, чем CompiledQueries.

InitQueryX() должен использовать несколько фиктивных входных данных, чтобы он охватывал все пути в методе QueryX () (что-то вроде покрытия кода модульных тестов).

Когда это возможно, входные данные InitQueryX() должны быть ложными, в результате чего в базе данных будет 0 строк, чтобы метод Init () занимал меньше времени.

Другие вопросы по тегам