#sql #linq #entity-framework #ef-code-first #generic-function
#sql #linq #entity-framework #ef-code-first #generic-function
Вопрос:
У меня есть контекст EF Code First Db, который я использую для запроса к базе данных. Я заметил некоторые проблемы с производительностью при передаче запросов как Func<Product, bool>
s из моего агрегированного репозитория, и при дальнейшем исследовании оказалось, что запросы не преобразуются в запросы SQL.
После еще небольшого изучения я обнаружил следующее.
var results = _context.Products
.Where(p => p.ProductCode.Contains("AAA"))
.Where(p => p.CategoryId == 1)
.ToList();
Это работает точно так, как ожидалось. Он генерирует некоторый параметризованный SQL с предложением Where .
==================================================================
var results2 = _context.Products
.Where(p => p.ProductCode.Contains("AAA") amp;amp; p.CategoryId == 1)
.ToList();
Это также работает, как и ожидалось. Он генерирует тот же sql, что и выше
==================================================================
Func<Product, bool> pred = (p => p.ProductCode.Contains("AAA") amp;amp; p.CategoryId == 1);
var results3 = _context.Products.Where(pred).ToList();
Это не работает. Он не генерирует предложение where в SQL, он возвращает все, а затем фильтрует это в коде.
Ответ №1:
Потому что для перевода в SQL он должен быть an Expression<...>
, а не a Func<...>
.
Это делается автоматически для вас компилятором, и поскольку перегрузки классов Linq-to-SQL принимают выражения, а не делегаты, компилятор автоматически преобразует ваш код (который выглядит как лямбда или анонимный метод) в объект expression и передает его.
Однако, если вы позаботитесь о создании функции самостоятельно, компилятор не сможет этого сделать, а Linq-to-SQL не использует анонимные методы, он принимает только выражения.
Что вы можете сделать, так это выполнить те части вашего запроса, которые вы можете, а затем отфильтровать результаты с помощью вашей функции, но я бы подумал о том, чтобы просто изменить тип вашего значения на выражение вместо этого.
Комментарии:
1. Спасибо Лассе, да, у меня было это озарение после отправки Q.
Ответ №2:
Как только я опубликовал это, ReSharper помог ответить на мой вопрос, показав мне сигнатуру метода перегрузки для метода Where()
расширения.
Для этого требуется оба Func<T, bool>
и Expression<Func<T, bool>>
. Если вы объявляете свои предикаты извне, вы должны использовать вариант выражения, поскольку первый не преобразуется в sql.
Ответ №3:
Вот почему запрос считывает всю таблицу целиком.
Когда вместо an Func
используется a Expression
, компилятор выбирает методы on System.Linq.Enumerable
— вместо System.Linq.Queryable
. Enumerable
Методы перебирают исходную коллекцию (иногда лениво), в то время Queryable
как методы создают дерево выражений.
Поскольку вызов Where
не является частью дерева выражений, генератор sql не видит его во время преобразования запроса.