我發現現在有不少博友,都反對使用EF框架,說它性能低.其實只要你用的好,性能不是問題,經過測試,它也會接近ado.net的訪問了.
當然如果對EF不了解,隨便亂用,確實會引來性能問題.因為EF的查詢語句都是自己生成的.如果不注意,它會多次查詢數據庫,或用效率不高的語句去查詢.
下面我就把我們在項目中遇到的問題,現我把他總結出來.以供大家參考.當然還有一些沒有列出來的,希望各網友也一起提供一下,以避免大家少走彎路.
- 分頁的時候,盡量在數據庫里面去分頁.在我實際中的項目,我就發現我同事由於他不了解EF屬性,它的分頁都是做在內存中分頁.下面請看他的代碼.
queryToList().Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);
像上面的語句,他會先把數據從數據庫中查出來,然后才分頁.
正確的寫法應如下:
query.Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize).ToList();這樣才會在數據庫中分頁.
2.盡量禁用延遲加載,盡量使用預加載和顯式加載查詢.
Vs 默認生成的代碼,是啟用了延遲加載的.這樣往往是有些開發人員在不了解的情況下,進行了多次往數據庫查詢.如下的代碼.
using (SchoolContainer db = new SchoolContainer())
{
var list = db.People.Where(ent => ent.PersonID < 30).ToList();
foreach (var people in list)
{
foreach (var ent in people.StudentGrades)
{
Console.WriteLine(ent.Grade);
}
}
}
如果啟用延遲加載,這樣會造成多次往返數據庫查詢的.勢必造成性能低下.
因此我們在這里應該使用預加載.代碼如下:
var list = db.People.Include("StudentGrades").Where(ent => ent.PersonID < 30).ToList();
當然使用了預加載,延遲加載也不會查詢多次.但怕在程序員寫代碼時,忘了要預加載,如果啟用了延遲加載,那么就會多次查詢數據庫.如果不啟用,那么程序員就獲取不了數據,這樣他就馬上明白,要進行預加載了.
當然任何事情不是絕對的.如果被查詢的對象,過於復雜,就要慎用預加載
3.注意事務的簡短性.
在使用事務時,我們要盡量把查詢語句或者其他響事務外的語句移在事務外執行.不然讓一個事務的時間太長了,就容易引起資源死鎖的問題.我上次就優化我一個同事的代碼,他代碼里就把所所有不相關的東西都放在事務里執行,這樣就造成事務的時間太長,當用壓力測時,馬上就出現資源被鎖的錯誤.
4.查詢出來的實體,如果不考慮刪除和修改,請用NoTracking
關於Notracking 的使用方法,請看. http://www.cnblogs.com/LingzhiSun/archive/2011/04/27/EF_Trick4.html
5.批量刪除和修改,不要用先把實體查詢出來,然后再逐個刪除和修改.這樣會產生大量的語句,效率肯定會低.
對於這個解決方式一是直接用sql語句執行,二是可以用自己寫個擴展方法來操作.雖然有不少人反對這方法.但我個人是傾向於自己寫個擴展方法.這樣,有2個好處.一是給開發人員使用簡單.二是方便管理. 雖然也有網友提出用ObjectStateManager.ChangeObjectState 來實現批量刪除,但我是沒有找到相關的批量刪除方法.而且這個也經常會出異常.
6.使用已編譯的查詢,雖然到EF5.0, LINQ 查詢是自動緩存的.但使用編譯查詢會比自動緩存的效率高.
使用編譯查詢,請參照http://msdn.microsoft.com/zh-cn/library/bb896297.aspx.
7.預生成視圖,
具體操作如下:
8.還有一點,就是對於復雜的查詢,我們要隨時監控生成的查詢語句.
畢竟EF生成的語句,往往比我們生成的語句更加復雜,這個時候我們就要考慮是否通過其他方式來提高性能.
9.更多EF性能優化,請參考http://msdn.microsoft.com/zh-cn/library/cc853327.aspx