當你使用LINQ to SQL時,請使用工具(好比LINQPad)查看系統生成的SQL語句,這會幫你發現問題可能發生在何處。html
當咱們僅須要一個資料的時候,咱們能夠考慮使用First / FirstOrDefault / Take / Any等方法,它們都會在取得合乎要求的資料後退出,而不會遍歷整個序列(除非最後一個資料纔是合乎要求的哈哈)。而相似ToList / Max / Last / Sum / Contain等方法顯而易見會遍歷整個序列。面試
例如你判斷一個集合是否有成員時,請使用Any而不是Count==0。由於若是該集合有極多成員時,Count遍歷是很是消耗時間的。數據庫
若是你在重複枚舉同一個序列,你可能會收到以下的警告:數組
通常看到這個提示,你須要一個ToList/ToDictionary/ToArray等相似的方法。重複枚舉是沒必要要且浪費時間的。另外,若是程序涉及多線程,或者你的序列含有隨機因素,你的每次枚舉的結果可能不一樣。咱們只須要枚舉同一序列一次,以後將結果儲存爲一個泛型集合便可。緩存
例如咱們的序列帶有隨機數:多線程
此時咱們會遍歷序列四次。但每次序列都會不一樣。例如若是咱們呼叫Sum方法四次,則可能會出現4個不一樣的和。咱們必須使用ToList方法強制LINQ提早執行。工具
在得到序列最後一個成員時,咱們有不少方法:性能
其中前兩個方法都不是最好的。當咱們調用LINQ的某些方法時,咱們緩存了整個序列,而這多是沒必要要的。咱們根本不須要將整個序列留在內存中,只須要得到最後一個成員就能夠了。單元測試
根據前面兩點,咱們能夠總結出來什麼時候使用ToList / ToArray / ToDictionary等方法:測試
是否返回IEnumerable<T>,或者返回一個List,或者數組?注意當你返回IEnumerable<T>時,你並無開始遍歷這個序列(只有當你強制LINQ執行時,纔會執行這個返回IEnumerable<T>的方法)。固然若是數據來自遠端,你還能夠選擇IQueryable<T>,它不會把資料一股腦拉下來,而是作完全部的篩選以後,才ToList,把資料從遠端下載下來。因此在使用ORM時,若是它用到了IQueryable,請將你的查詢也寫成表達式而不是委託的形式。參考:http://www.cnblogs.com/SieAppler/p/3501475.html
另外,咱們能夠經過返回IEnumerable<T>而不是List或數組,來給予呼叫者最大的便利。(給他一個最General類型的返回)
假設你有一個父表(例如:汽車),其關聯一個子表,例如輪子(一對多)。如今你想對於全部的父表汽車,遍歷全部汽車,而後打印出來全部輪子的信息。默認的作法將是:
SELECT CarId FROM Cars;
而後對於每一個汽車:
SELECT * FROM Wheel WHERE CarId = ?
這會SELECT 2個表一共N(子表的行數)+1(父表)次,故稱爲SELECT N+1問題。
考察下面的代碼。假設album是一個表,artist是另一個表,album和artist是一對多的關係:
咱們知道foreach會強制LINQ執行,因而,咱們能夠想象這也是一個SELECT N+1問題的例子:先得到全部album(SELECT * FROM ALBUM),而後遍歷,對每個album的Title,檢查其是否包含關鍵字,若是符合,再去SELECT 表artist,共SELECT N+1次。咱們能夠經過LINQPAD或其餘方式檢查編譯器生成的SELECT語句數目,必定會是N+1條SQL語句。
解決方法:使用一個匿名對象做爲中間表格,預先將兩個表join到一塊兒:
生成的SQL將只有一句話!
這篇文章中的第三點,就是一個典型的SELECT N+1問題。在代碼中,選擇了前100個score(一條SQL),而後對全部score進行遍歷,從表Student中得到Name的值(100條SQL)。
解決方法也在文章中給出了,就是將兩個表連到一塊兒。該文章的「聯表查詢統計」這一節,說的仍是這個問題。簡單說,仍是每次都用LINQPad工具,看看最終生成的SQL到底長啥樣。(固然還有不少其餘工具,或者最基本的就是用SQL Profiler不過比較麻煩)
提高從數據庫中拿數據的速度,能夠參考如下幾種方法:
咱們能夠經過不少工具來得到系統產生的SQL語句,例如LINQPAD或者SQL Profiler。在EF6中,咱們還可使用這樣的方法:
注意:編譯器不必定可以將你的LINQ語句翻譯爲SQL,例如字符串的IndexOf方法就不被支持。
LinqOptimizer能夠經過nuget得到。你能夠經過在IEnumerable<T>上調用AsQueryExpr方法來令LinqOptimizer優化你的LINQ語句。使用Run方法執行:
在沒有找到性能瓶頸以前,不要過早優化。
在什麼狀況下,LINQ反而不如Foreach表現好?二者的性能差距是怎樣的?下面的例子的序列有一千萬個成員,咱們對它們作些簡單運算。
結果:
能夠看到Foreach的表現稍好一點。LINQ的額外開銷在於將lambda表達式轉換爲委託的形式,而foreach不須要。雖然這一點點額外開銷對於普通的狀況基本能夠忽略,但若是重複一千萬次,則性能可能會有較爲明顯的差別。
顯而易見,若是咱們重複運行相同的任務,且任務之間又沒有什麼關係(不須要對結果進行彙總),此時咱們能夠想到用多線程來解決問題,重複利用系統的資源:
執行後只用了423毫秒。一般來講,執行的結果將等於Foreach的時間,除以系統CPU的核數量。當CPU爲雙核時,速度大概能夠提高一倍。固然,對於單核機器來講,PLINQ是沒有意義的。
當你的機器擁有多核,而且你處理相同的任務時(例如從不一樣的網站下載內容,並作相同的處理),能夠考慮使用PLINQ。不過PLINQ也須要一些額外開銷:它訪問線程池,新建線程,將任務分配到各個線程中,而後還要收集任務的結果。因此,你須要測量PLINQ是否真的能夠加快你的代碼的運行速度。
一般,只有在以下狀況下才會考慮將本身寫的ORM投入生產使用:
而大部分ORM開發出來的目標僅僅是:
一般,本身開發一套ORM須要很長的時間,才能保證沒有錯誤,並用於生產環境。大部分狀況下,EF已是一個不錯的選擇。性能是雙刃劍,它可能也會毀了你的代碼,讓你的代碼難以維護。