2014-10-28 3 views
4

У меня есть Func<ProductItemVendor, bool>, хранящийся в CompareProductItemVendorIds. Я хотел бы использовать это выражение в запросе LINQ.Использование Func <> в запросе LINQ

Он появляется следующее юридическое:

var results = 
    Repository.Query<ProductItemVendor>().Where(CompareProductItemVendorIds); 

Однако следующий не является законным:

var results = from v in Repository.Query<ProductItemVendor>() 
       where CompareProductItemVendorIds(v) 
       select v; 

Этот код выдает ошибку:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

Вопросы:

  1. Почему эти заявления настолько разные, что мой Func<> является законным в одном, но не другом? Я думал, что они оба в основном сделали то же самое.

  2. Как это сделать? Нужно ли мне объяснять, создать Func<> вместо Expression<Func<>>?

См. Мой родственный вопрос: Using Expression<Func<>> in a LINQ Query.

+0

нуб вопрос: 'CompareProductItemVendorIds 'в первом примере совпадает с' CompareProductItemVendorIds (v) 'во втором? – Kapol

+0

@ Kapol: Да, здесь хранится мой 'Func <>', который я описал в первом абзаце. –

+0

Посмотрите на [LINQKit] (http://www.albahari.com/nutshell/linqkit.aspx), чтобы помочь вам составить запросы от выражений. –

ответ

9

Существует большая разница между Expression<Func<T,bool>> и Func<T,bool>. Первый - это дерево выражений. Вы можете рассматривать это как описание кода. Для Linq to Entities нужны деревья выражений. Потому что ему нужно построить SQL-запрос. Таким образом, ему нужно описать код для перевода одних и тех же действий в SQL.

Второй, Func<T,bool> - простой способ с указанной подписью. Здесь ничего особенного. Почему его законным здесь:

Repository.Query<ProductItemVendor>().Where(CompareProductItemVendorIds); 

Это просто. Существует два метода расширения Where. Один fore IQueryable<T>, который ожидает дерево выражений (которое будет переведено в SQL-запрос). И еще одно расширение для IEnumerable<T>, которое ожидает ординарный метод фильтрации внутри памяти (обычный код C#). Таким образом, у вас нет дерева выражений, последний выбран. Здесь не генерируется SQL. Это ваше дело.

Теперь второй вопрос:

from v in Repository.Query<ProductItemVendor>() 
where CompareProductItemVendorIds(v) 
select v 

На самом деле это не тот же запрос. Это эквивалентно

Repository.Query<ProductItemVendor>().Where(v => CompareProductItemVendorIds(v)); 

И здесь у вас есть лямбда-выражение, которое может быть преобразовано в дерево выражений. И еще один удлинитель Where - один для IQueryable<T>. Таким образом, Linq to Entities пытается преобразовать это дерево выражений в SQL. Но что он должен преобразовать? Да, вызов некоторого встроенного метода. И, конечно, Linq to Entities не может этого сделать.

Для того чтобы ваш запрос работал, вы должны использовать Expression<Func<T,bool>>. Вы можете создать его вручную или использовать лямбда-выражение.

+0

Спасибо. Да, я знаю разницу между «Func <>» и «Expression >». Вы говорите, что в первом случае список материализуется первым, а затем 'Where()' вызывается в материализованном списке? –

+0

@JonathanWood да, точно. Вы получаете все данные в память, а затем собираете коллекцию в памяти. Вы можете проверить его с помощью профилировщика EF или SQL –

+1

@JonathanWood да, когда вы вызываете ['Enumberable.Where'] (http://msdn.microsoft.com/en-us/library/bb534803 (v = vs.110). aspx) в объекте IQueryable, он будет реализовывать результаты перед выполнением. Чтобы получить список, который не будет реализован сначала, вы должны вызвать ['Queryable.Where'] (http://msdn.microsoft.com/en-us/library/bb535040 (v = vs.100) .ASPX), который вы можете см. только выражение. –

4

Причина, по которой ваша первая версия работает, - это Linq, слишком умный, потому что это хорошо.Эквивалент вашей первой версии на самом деле

IEnumerable<ProductItemVendor> temp = Repository.Query<ProductItemVendor>().AsEnumerable(); 
var results = temp.Where(CompareProductItemVendorIds); 

Итак, когда вы выполняете запрос вы возвращаете каждую строку в базе данных, то выполнение Where в памяти на локальном компьютере.

Чтобы получить предложение Where, которое должно быть выполнено в базе данных, вы должны изменить тип CompareProductItemVendorIds как Expression<Func<ProductItemVendor, bool>>.

Там нет никакого способа, чтобы «преобразовать» из Func<TIn, TOut> в Expression<Func<TIn. Tout>>, вы должны переписать код, чтобы быть изначально выражение, можно затем преобразовать в Func<TIn, TOut> по телефону CompareProductItemVendorIds.Compile()

Expression<Func<ProductItemVendor, bool>> CompareProductItemVendorIds = //... 
Func<ProductItemVendor, bool> CompareProductItemVendorIdsAsFunc = CompareProductItemVendorIds.Compile(); 
+0

+1 Спасибо за объяснение. –