2016-05-06 62 views
2

考慮以下功能:爲什麼Queryable.SelectMany(...)重載接受Func <S,IEnumerable <R>>而不是Func <S,IQueryable <R>>?

IQueryable<Bar> foo(IEnumerable<IQueryable<Bar>> sources) 
{ 
    return 
     from source in sources.AsQueryable() 
     from bar in source 
     where bar.Xzy == 123 
     select bar; 
} 

直覺上,我希望它可以在每個源的上下文中執行的「從...這裏......選擇」表達。不過,我相信它只會針對源執行「from ... in ...」部分,而不是執行「where ... select」部分作爲LINQ-to-Objects查詢。最終的結果是SomeTable的所有行將從每個源檢索,而不是隻匹配「where」條件的那些。

乍一看,我的猜測是,這是因爲對SelectMany的調用導致「源」表達式被隱式轉換爲IEnumerable <Bar>。我不確定實現是什麼樣子,但是接受Func < S,IQueryable <R> > >而不是合理的,這樣where ... select表達式就傳遞給IQueryable提供者了嗎?

+0

「source.SomeTable」的類型是什麼?如果將'foo'的返回類型更改爲'IQueryable ',會發生什麼? – Lee

+0

@Lee,對不起,我認爲SomeTable屬性在示例中沒有意義。我將「source.SomeTable」改爲「source」。我認爲將返回類型更改爲IQueryable 不會更改查詢的行爲。 – Aaron

+0

返回類型應做出不同,因爲'IEnumerable '重載返回'IEnumerable ',而'IQueryable'重載返回'IQueryable '。因此,如果返回類型更改爲「IQueryable 」,並且選擇了「SelectMany」的IEnumerable超載,那麼您的函數將無法編譯。 – Lee

回答

1

我認爲這歸結於編譯器如何解釋您的查詢。什麼它實際上是將你的查詢轉換是這樣的(嚴格的左到右解析):

sources.AsQueryable() 
     .SelectMany(source => source) 
     .Where(bar => bar.Xzy == 123) 
     .Select(bar => bar) 

重要的是,你的過濾器上的第一枚舉每個源的結果進行操作。

還要注意的是,實際上AsQueryable()是多餘的,因爲它不是讓你的來源還有任何可查詢比他們已經是,它使得枚舉你的源代碼可查詢,並且你沒有查詢該設置反正,你正在查詢個人來源。

你真正想要的是更喜歡這個,我覺得:

sources.SelectMany(source => source.Where(bar => bar.Xzy == 123)) 

這改變了相對的「訂單的優先級」條款的解釋。

我實際上不知道如何使用LINQ語法來創建後者。

UPDATE:其實,這裏有一個方法:

from source in sources 
let qsource = (from bar in source 
       where bar.Xzy == 123 
       select bar) 
from result in qsource 
select result 

我一般傾向於更喜歡使用手藝由於這個原因擴展方法不平凡的查詢。

+0

謝謝,我使用LINQ表達式就像在更新的答案中一樣。只是試圖繞過我的頭,爲什麼原來的查詢沒有做到我所期望的。我最初在我的「真實」查詢中添加了AsQueryable()調用,它認爲它通過強制使用Queryable.SelectMany而不是Enumerable.SelectMany來解決我的問題(它沒有)。 – Aaron

相關問題