2011-09-30 81 views
23

請看這行代碼。這是一個存儲過程的調用,它返回ObjectResult<long?>。爲了提取長值,我增加了選擇:.NET實體框架 - IEnumerable VS. IQueryable

dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value); 

基於智能感知這個選擇返回IEnumerable<long>

我不知道我是在某處閱讀,還是習慣了這個假設 - 我一直認爲當EF API返回IEnumerable(而不是)時,這意味着結果已經實現。意思是他們已經從數據庫中提取出來了。

我今天發現我錯了(或者這可能是一個錯誤?)。我一直得到錯誤

「新的事物是不允許的,因爲有其他線程 在會話中運行」

基本上,這個錯誤告訴你,你想救而變化數據庫讀取器仍在讀取記錄。

最後我解決它的(我認爲是一個長鏡頭),並加入ToArray()呼叫物化爲IEnumerable<long> ...

所以 - 底線 - 我應該期待EF IEnumerable結果包含結果避風港物化了嗎?如果是,那麼有沒有辦法知道IEnumerable是否已經實現?

感謝,如果是這樣的那些「duhhh」的問題之一道歉... :)

+0

我不確定是否有任何特別記錄的EF,但是對於Linq來說,'IEnumerable '不比'IQueryable '更具體化 - 兩者通常都假定使用延遲執行。 –

+0

@Damien_The_Unbeliever:但是,我認爲ObjectResult 將始終執行時調用該函數...(在這種情況下FindCoursesWithKeywords) –

回答

27

IQueryable在您使用Linq-to-entities =時使用=您正在應用程序中構建聲明式LINQ查詢,該查詢將被LINQ提供程序解釋爲SQL並在服務器上執行。一旦查詢被執行(迭代),它將變成IEnumerable,並且對象將按照迭代的需要實現=不是立即。

一旦你調用存儲過程,你沒有使用Linq-to-entities,因爲在你的應用程序中沒有內置的聲明性查詢。查詢/ SQL已經存在於數據庫服務器上,您只需調用它即可。這將返回IEnumerable,但它不會立即實現所有結果。結果將按照迭代的方式實現。當您明確要求提取對象時,這是數據庫遊標/或.NET數據讀取器的原理。

所以,如果你是這樣的:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords) 
           .Select(l => l.Value)) 
{ 
    ... 
} 

您獲取課程逐一(順便說一句,如果你有興趣只在關鍵字爲什麼加載全過程。?)。在您完成或打破循環之前,您的數據讀取器將被打開以獲取記錄。

如果改爲調用這個:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords) 
           .ToList() // or ToArray 
           .Select(l => l.Value)) 
{ 
    ... 
} 

您將強制查詢立即兌現所有的結果和循環將在內存中,而不是打開的數據庫讀者對集合執行。

IEnumerableIQueryable之間的區別並不在於如何提取數據,因爲IQueryableIEnumerable。區別在於支持結構(必須實現這些接口)。

+0

嗨,感謝您的解釋!因此,由於這是一個存儲過程,它不會返回IQueryable,因爲它不支持查詢所做的所有功能,對吧? (順便說一句,我提取的課程不是關鍵字):) – justabuzz

10

IEnumerable<T>工作意味着所有進一步的操作將在C#代碼,即LINQ到對象發生。這並不意味着查詢已經執行。

一旦你降級到linq-to-objects,此時剩下的所有數據都需要從數據庫中提取併發送到.net。這會嚴重降低性能(例如,數據庫索引不會被linq-to-objects使用),但另一方面,linq-to-objects更靈活,因爲它可以執行任意C#代碼,而不受限於什麼您的linq提供程序可以轉換爲SQL。

A IEnumerable<T>可以是延遲查詢或已經物化的數據。標準的linq運營商通常會被推遲,並且總是會實現ToArray()/ToList()

+0

感謝您的區別!那麼,我應該注意的是什麼?是否有任何影響或陷阱?乾杯! – justabuzz

+0

最明顯的問題是退化爲'IEnumerable '會大幅降低性能。想象一下,在查詢開始的時候你會降級,那麼很可能需要提取整個表,而不是使用服務器上的SQL使用數據庫上的索引進行過濾以加速。所以在你降級到'IEnumerable '的時候,你希望儘可能少的東西剩下。 – CodesInChaos

+0

不太確定這是否正確。進一步閱讀我做了+在這裏的其他答案說一個類似的東西 - IEnumerable 也可能不會實現,直到後來當你迭代它。如果我明白這是正確的,這是因爲當你執行一個存儲過程時,你會得到一個完整的IQueryable,因爲這不是一個完整的可查詢對象。說得通? :) – justabuzz

-5

IEnumerable:LINQ to Object和LINQ to XML。

IQueryable:LINQ to SQL

+0

IQueryable是一種尚未執行的類型。基本上它是「查詢」。 http://msdn.microsoft.com/en-us/library/system.linq.iqueryable(v=vs.100).ASPX 病毒與LINQ to SQL或Linq to Anything無關。它是提供對任何類型的數據提供者訪問的接口。 – Tony

0

IEnumerable將不會水合,直到物化。如果調用一個存儲過程,我會認爲沒有其他過濾器需要,我的意思是你發送參數到一個存儲過程來產生返回的所需數據子集。與存儲過程綁定的IEnumerable沒問題。但是,如果您要獲取表格的全部內容,那麼在應用程序中篩選應該有一個策略。就像,不要ToList()表的IEnumerable,你將實現所有行。有時候這會導致內存不足異常。此外爲什麼消耗記憶沒有理由。對上下文使用IQueryable,這樣你可以在數據源過濾表,而不是在應用程序中。作爲回答,你必須實現它。 IEnumerable是一個接口,只有實現它才能「初始化」它的類型併產生我理解的東西。