EF和SqlHelper 簡單三層 EF生成sql,再調用ado.net訪問資料庫,最後使結果對象具體化. 之前的SqlHelper 簡單三層的寫法,拼接sql語句字元串,再調用ado.net訪問資料庫,最後也是把結果轉換為對象. 明顯的區別:sql語句的產生,EF是SQL查詢命令和 LINQ 查詢 ...
EF和SqlHelper 簡單三層
- EF生成sql,再調用ado.net訪問資料庫,最後使結果對象具體化.
- 之前的SqlHelper 簡單三層的寫法,拼接sql語句字元串,再調用ado.net訪問資料庫,最後也是把結果轉換為對象.
明顯的區別:sql語句的產生,EF是SQL查詢命令和 LINQ 查詢生成,SqlHelper簡單三層是程式員直接拼接sql語句.
那麼,一直談EF性能,不得不說EF產生sql的速度.
回顧上篇文章提到的 EntityFramework性能之預生成視圖 ,裡面有張 查詢執行的各個階段 圖解.
各個階段如下:
- 載入元數據,中等成本,在每個應用程式域中一次.
- 打開資料庫連接.(使用ado.net也免不了.)
- 生成視圖,成本雖高,卻在每個應用程式域中指執行一次.(上篇文章折騰了半天,最終覺得EF6.1.3和.net 4.5已經優化過了,不必在意這個預生成視圖.)
- 準備查詢,中等成本.每個唯一查詢一次。註釋:因為實體 SQL查詢命令和 LINQ 查詢現已緩存,所以,以後執行相同查詢所需的時間較少。 您仍可以使用已編譯的 LINQ 查詢來降低後續執行中的這一開銷,編譯的查詢比自動緩存的 LINQ 查詢效率更高。
- 執行查詢,低成本,每個查詢一次。註釋:使用 ADO.NET 數據提供程式對數據源執行命令的成本。 因為大多數數據源緩存查詢計劃,所以,以後執行相同查詢所需的時間可能較少。
- 載入和驗證類型,跟蹤,使對象具體化
建立項目
(EF中還是用之前的PhoneBookModel.edmx,熟悉的名字.)
這次用asp.net mvc.在Home控制器下,有兩個方法.
public ActionResult PreHot() { var db = new PhoneBookEntities(); db.ContactInGroup.ToList(); return View(); }PreHot
public ActionResult Test() { Stopwatch sw = new Stopwatch(); sw.Start(); using (var db = new PhoneBookEntities()) { var gi = db.GroupInfo.FirstOrDefault(c => c.GroupName.Contains("g1!")); var ci = db.ContactInfo.FirstOrDefault(c => c.ID == 12); ci.ContactName += "!"; gi.GroupName += "!"; using (var tx = db.Database.BeginTransaction()) { try { db.Database.ExecuteSqlCommand("update GroupInfo set GroupName='hello' where GroupId=209"); db.SaveChanges(); tx.Commit();//此語句不要漏了,否則監控結果會是釋放了事務,而不是提交了事務! } catch (Exception) { tx.Rollback(); } } } sw.Stop(); ViewBag.time = sw.ElapsedMilliseconds;//在視圖裡顯示花費的時間 return View(); }Test
註意:之前文章提過,怎麼監控sql語句.而在監控記錄里,會給出執行sql語句的時間.對於Test方法中EF生成的sql語句,
記錄顯示: 執行-- 已在 1 毫秒內完成,結果為: SqlDataReader.
測試開始:
- a操作.清理解決方案,生成,先訪問/home/index,再直接訪問 /Home/Test,用時1250毫秒,再次訪問 /Home/Test ,時間顯示2-15毫秒.(/home/index,裡面沒任何代碼,僅用來啟動網站)
- b操作.清理解決方案,生成,先訪問/home/index,之後訪問/Home/PreHot,再訪問 /Home/Test,顯示時間 250毫秒,再次訪問 /Home/Test 時間顯示2-15毫秒.
對於這個結果,我的解釋是:
a操作,訪問/Home/Test,執行Test()方法,而此時,要走 查詢執行階段的1,2,3,4,5,6,對比之前的 查詢執行的各個階段,就明白為什麼會用時1250毫秒這麼久(相比資料庫執行查詢只需要1毫秒.);
再次訪問/Home/Test時, 查詢執行階段的1和3不用走(應用程式域中一次),4也不走(每個唯一查詢一次[Test()方法里的查詢執行過一次了,不唯一了,查詢已自動緩存]),5執行查詢(使用了緩存查詢計劃,使以後執行相同查詢所需的時間可能較少),再走6.所以用時大幅度降低到2-15毫秒(顯示時間大多在5毫秒左右,給個小公式:5毫秒=執行sql語句用時1毫秒+EF產生sql語句用時4毫秒).
b操作,訪問/Home/PreHot,先讓 查詢執行階段的1和3走了一次.再訪問 /Home/Test,走4,5,6,顯示時間 250毫秒(對比a操作首次訪問 /Home/Test,少了對性能影響較大的1和3階段).
再次訪問 /Home/Test 時間顯示2-15毫秒(同a操作).
也就是說,經過EF的初次使用,再加上 實體 SQL查詢命令和 LINQ 查詢 被自動緩存,滿足這兩個條件後,生成sql語句速度極快(應該不會大於5毫秒).
如果一個網站使用的是EF,稍微有點訪問量,EF生成sql語句的速度已不是問題.(同樣的測試Test()方法,在控制台里,時間一直是350毫秒左右...?)
生成sql語句的速度已不是問題,那麼接下來就是生成sql語句的質量.之前的博文,希望大家看過.
如果EF生成sql的質量夠好,EF的效率就不是問題,我感覺其效率會無限接近原生的ado.net.
下篇,會拿 SqlHelper操作Ado.net跟使用EF做個對比.
如果,你初學EF,有一個表ContactInfo,含100W行數據, 而你寫了一行這樣的代碼:
var res= db.ContactInfo.ToList().Skip(5).Take(5);
發現速度極慢,你就想EF真垃圾,我不使用EF,毫秒中搞定.再也不看EF啦.那就是你的損失了.自已用的不好,卻在怪EF不行.
Ado.net和EF的關係,就好比c跟java和c#的關係.
理論上說,c的效率要比java和c#高.但java和c#還是被廣泛使用.
但是,同樣的功能,同樣用c#去寫,有的人寫的程式會高效.因為他在用c#語言中,會避開耗性能的拆箱裝箱,會更高效地去使用StringBuilder去拼接比較複雜的字元等.
c#語言本身的好的.但是程式員能不能高效地使用它又是另外一回事了.
同樣的,EF也是好的,就看你對它的瞭解有多深入了.
會Ado.net,知道怎麼寫sql更優,明白怎麼監控EF產生的sql語句,就會對瓶頸進行調優,就能把EF變成自己的利器.這也就是我先看EF產生的sql的原因.
如果什麼都不知道,就用EF一頓瞎寫,就好比用webForm一頓拖伺服器控制項, 把網址拖慢了,把人拖的腦殘了(只會拖控制項,換個jsp就懵逼了...估計控制項拖的連請求,處理,響應都弄不明白.)
(博客園 dudu 關於EF的文章,我大多看了,不過版本是EF4.根據 dudu博文,博客園也是用了EF這個利器的.)
您認為EF如何呢?