Почему отдельный экземпляр предиката Func<T, bool> не переводится в SQL с Entity Framework?
У меня есть контекст EF Code First Db, который я использую для запроса к базе данных. Я заметил некоторые проблемы с производительностью при передаче запросов как Func<Product, bool>
Из моего Агрегированного репозитория и при дальнейших исследованиях выяснилось, что запросы не переводятся в 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") && p.CategoryId == 1)
.ToList();
Это также работает как ожидалось. Генерирует тот же sql как выше
================================================== ================
Func<Product, bool> pred = (p => p.ProductCode.Contains("AAA") && p.CategoryId == 1);
var results3 = _context.Products.Where(pred).ToList();
Это сломано. Он не генерирует предложение where в SQL, он возвращает все и затем фильтрует его в коде.
3 ответа
Потому что для того, чтобы перевести на SQL, он должен быть Expression<...>
не Func<...>
,
Это делается автоматически для вас компилятором, и поскольку перегрузки в классах Linq-to-SQL принимают выражения, а не делегаты, компилятор автоматически преобразует ваш код (который выглядит как лямбда или анонимный метод) в объект выражения и передать это.
Однако, если вы позаботитесь о создании функции самостоятельно, компилятор не сможет этого сделать, а Linq-to-SQL не использует анонимные методы, он принимает только выражения.
Что вы можете сделать, так это выполнить те части вашего запроса, которые вы можете, и затем отфильтровать результаты через вашу функцию, но я бы хотел просто изменить тип вашего значения на выражение.
Не успел я опубликовать этот ReSharper, который помог мне ответить на мой вопрос и показал подпись метода перегрузки для Where()
метод расширения.
Требуется как Func<T, bool>
а также Expression<Func<T, bool>>
, Если вы объявляете свои предикаты извне, вы должны использовать вариант Expression, поскольку первый не переводится в sql.
Вот почему запрос читает всю таблицу.
Когда Func
используется вместо Expression
компилятор выбирает методы на System.Linq.Enumerable
- вместо System.Linq.Queryable
, Enumerable
методы повторяют исходную коллекцию (иногда лениво), в то время как Queryable
методы строят дерево выражений.
Поскольку призыв к Where
не является частью дерева выражений, генератор sql не видит его во время перевода запроса.