Блог

Михаил Шишло, Senior .NET разработчик

Как Entity Framework оптимизирует запросы

Работая на одном из наших проектом, наткнулся на очень интересную особенность Entity Framework для оптимизации запросов. Спешу с Вами поделиться, так как поведение весьма нетривиально и выяснение причины заняло около 2 часов с постепенным  привлечением к процессу поиска всей нашей дев команды.

Итак, сразу с места в карьер. Имеется следующий код для маппинга таблицы с помощью EF Code First

 

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

И есть вот такой запрос

Простой запрос по выборке данных из таблицы “T_InstallmentSchedule” с несколькими условиями. Хотя и в таблице присутствовали  данные, удовлетворяющие всем критериям в запросе, но в результате не было ни одной записи. Посмотрел какой sql запрос генерирует entity framework и очень удивился, т.к в результате был такой код:

 

Поискав в google по ключевым словам, стало понятно, что это EF оптимизируют запрос и вообще не обращается к таблице “T_InstallmentSchedule”. Но что же именно спровоцировало данное поведение. Поочередно убирая условия фильтрации из запроса, нашел причинy в этом условии !x.InstallmentPaymentID.HasValue.

Т.е EF не смотрит на другие условия в запросе и считает что если внешний ключ не установлен, то по умолчанию результат не должен содержать записей.
Но почему так? Ведь у нас внешний ключ “InstallmentPaymentID” обозначен как IsOptional().

А причина оказалось в том, что связь таблиц “T_InstallmentSchedule” и “T_InstallmentPayment” по ошибке была указана как required:

    HasRequired(a => a.InstallmentPayment).WithMany(a => a.InstallmentSchedules).HasForeignKey(a => a.InstallmentPaymentID);

И EF считал, что раз связь required, то и запрос, в котором внешний ключ идет со значением null, по умолчанию не может вернуть записей. Все решалось заменой в маппинге исходной  строки на следующую:

    HasOptional(a => a.InstallmentPayment).WithMany(a => a.InstallmentSchedules).HasForeignKey(a => a.InstallmentPaymentID);

Выводы:

  1. Вдумчиво пользуйтесь copy-paste, большинство ошибок делается по невнимательности
  2. Изучайте sql который генерирует ORM framework, можете встретить множество интересных вещей
  3. Один из основных инструментов разработчика – Google. Важно правильно искать по ключевым словам
  4. Если зашли в тупик, привлекайте коллег к поиску решению проблемы. Незамыленный взгляд стороннего человека помогает быстрее найти пути решения

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


пять + 7 =

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">