2014-10-28 80 views
4

我有一個Func<ProductItemVendor, bool>存儲在CompareProductItemVendorIds。我想在LINQ查詢中使用該表達式。在LINQ查詢中使用Func <>

看來下面是合法的:

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 2014-10-28 20:17:29

+0

@Kapol:是的,那是我的'Func <>'存儲的地方,我在第一段中描述過。 – 2014-10-28 20:18:18

+0

查看[LINQKit](http://www.albahari.com/nutshell/linqkit.aspx)以幫助您編寫表達式的查詢。 – 2014-10-28 20:30:32

回答

9

Expression<Func<T,bool>>Func<T,bool>之間有很大的差異。第一個是表達式樹。你可以把它看作代碼的描述。 Linq to Entities需要表達式樹。因爲它需要建立SQL查詢。所以它需要描述代碼才能將相同的動作轉換爲SQL。

第二個,Func<T,bool>是一個具有指定簽名的簡單方法。沒什麼特別的。爲什麼它的法律在這裏:

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

這很簡單。有兩種Where擴展方法。其中之一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)); 

在這裏你有lambda表達式,它可以轉換成表達式樹。另外使用另一個Where擴展名 - 一個用於IQueryable<T>。所以,Linq to Entities試圖將這個表達式樹轉換爲SQL。但它應該轉換什麼?是的,調用一些內存中的方法。當然,Linq to Entities沒有這樣做。

爲了使您的查詢工作,您應該使用Expression<Func<T,bool>>。您可以手動構建它,也可以使用lambda表達式。

+0

謝謝。是的,我知道'Func <>'和'Expression >之間的區別。你是說,在第一種情況下,列表首先被物化,然後在物化列表上調用「Where()」? – 2014-10-28 20:34:21

+0

@JonathanWood確實如此。您將所有數據存入內存,然後過濾內存中的集合。你可以用EF或SQL profiler來檢查它 – 2014-10-28 20:35:15

+1

@JonathanWood是的,當你調用['Enumberable.Where'](http://msdn.microsoft.com/en-us/library/bb534803(v = vs.110))。 aspx)在一個IQueryable對象上,它會在執行where之前實現結果。要讓列表不首先被實現,你必須調用['Queryable.Where'](http://msdn.microsoft.com/en-us/library/bb535040(v = vs.100).ASPX),你可以只看見表達式。 – 2014-10-28 20:35:33

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感謝您的解釋。 – 2014-10-28 21:00:29