2012-06-13 79 views
1

以下linq2entities代碼似乎會調用FirstOrDefault擴展的IEnumerable版本,因爲PersonH​​istories是一個ICollection。但是在運行時,它實際上調用了IQueryable版本。隱式AsQueryable

var test = db.Persons.Where(d => d.PersonKey == 4) 
     .Select(f => f.PersonHistories.FirstOrDefault()); 

我遇到的問題是,我使用的是不執行該自動轉換的自定義查詢供應商,我得到的錯誤「...的ICollection不能被用於類型的IQueryable的參數」。因此,需要顯式調用AsQueryable已到解決這個問題,但對於複雜的查詢,它會非常多餘的,不是感覺很乾:

db.Persons.Where(d => d.PersonKey == 4) 
     .Select(f => f.PersonHistories.AsQueryable().FirstOrDefault()); 

我已經在框架參考源周圍挖試圖找到LINQ的2實體訪客/供應商東西,但沒有運氣(也許不是任何開放參考資源的一部分)。 基礎提供者如何完成AsQueryable的這種隱式使用?


我明白,這些被翻譯成表達式樹。 我明白,提供者將Enumerable.FirstOrDefault替換爲Queryable.FirstOrDefault。這是問題的前提。

+0

嘗試翻轉它,並看到它「只是工作」:'db.Persons.Where(d => d.PersonKey == 4).PersonH​​istories.FirstOrDefault()' – bluevector

+0

不,不起作用,因爲.Where返回一個集合/ IQueryable,其上沒有這樣的PersonH​​istories字段。你將不得不使用類似SingleOrDefault的東西來獲取單個項目,或者使用.Select來投影嵌套字段。我選擇用嵌套投影演示問題,因爲這是我遇到問題的地方。由於db.Persons是一個DbSet,因此它是IQueryable,因此.Where自動成爲IQueryable版本,但相比之下,Person.PersonH​​istories作爲ICollection實現,處理起來更具挑戰性。 – AaronLS

回答

2

基礎提供程序如何完成AsQueryable的這種隱式使用?

他們不知道。你的代碼根本不是執行FirstOrDefault()。它構建了一個表達式樹,它表示該調用,但不是直接執行。查詢提供程序發現,確定f.PersonHistories實際上基於數據庫中的實體,並適當地轉換查詢。

+0

那麼表達式樹包含'IEnumerable'和'IQueryable'的單獨分支? –

+0

@JoelEtherton:表達式樹本身將包含對被調用方法的引用,該引用可以是'Enumerable.FirstOrDefault'或'Queryable.FirstOrDefault'。在這種情況下,樹實際上會引用'Enumerable.FirstOrDefault',但好的提供者會處理它。 –

+0

我知道它被轉換爲表達式樹,但表達式樹實際上包含一個'.Call System.Linq.Queryable.FirstOrDefault'(當使用默認提供者時)。但是它怎麼知道要這樣做呢?我的問題是,它是如何發現Queryable版本而不是IEnumerable版本的? FirstOrDefault的Queryable版本的代碼確實構建了一個表達式,但IEnumerable版本不執行任何此類檢查。允許它跳躍的機制在哪裏? – AaronLS