寫在開頭:本文的評論者大多認為我這個測試不對,但是哪里不對沒有誰給出一個明確的回復;對於若干純粹謾罵的評論(似乎我說EF性能低==侮辱了他全家),我已刪除。我的目的就是插入7千條數據到數據庫中,得出的結論是在數據Add到上下文這個階段比較耗時,如果有能繞過這個過程的方法,或者改進的建議,請提出,否則我不認為EF在這個場景中性能低下的結論是錯誤的!
為了不“激怒”更多人,標題都改了好幾次。當時寫這篇文章並不是為了證明什么,純粹是我在運行某項目的過程中“發現”了EF的某個瓶頸,遍尋解決方案未果,所以記錄下來。唉,怪只怪我太單純了……解決方法在文章最后。
另外關於在SaveChanges()時候會將生成的SQL語句一次性提交到數據庫的言論也可以消了,后面有部分評論持這個觀點,首先我一再強調瓶頸不在數據庫交互階段;為了避免類似無意義的評論反復出現,麻煩在吐槽之前用Profiler跟蹤一下。
早就聽說EF的性能不咋地,沒想到真的不咋地,而且還不是一般的不咋地(一句玩笑話引來一群人口誅筆伐,不明真相的博主表示很淡定)。有圖有真相,已經在項目中應用了EF的朋友慎入!
1、.NET4.0,EF4.4
1 public void ExecRealTimeRun(List<RealTimeStocks> realTimeData) 2 { 3 using (var context = new StockDataEntities()) 4 { 5 context.Database.ExecuteSqlCommand("delete from RealTimeStocks"); 6 7 var now1 = DateTime.Now.TimeOfDay; 8 Console.WriteLine(string.Format("{0}開始將數據Add到上下文中,數據量:{1}", now1, realTimeData.Count)); 9 foreach (var data in realTimeData) 10 { 11 context.RealTimeStocks.Add(data); 12 } 13 14 var now2 = DateTime.Now.TimeOfDay; 15 Console.WriteLine(string.Format("{0}數據Added完畢,開始執行Insert操作,耗時{1}", now2, now2 - now1)); 16 try 17 { 18 context.SaveChanges(); 19 } 20 catch (DbEntityValidationException dbEx) 21 { } 22 catch 23 { } 24 25 var now3 = DateTime.Now.TimeOfDay; 26 Console.WriteLine(string.Format("{0}Insert完畢,耗時{1}", now3, now3 - now2)); 27 } 28 }
很簡單,木有多余邏輯,結果:
插入7K多數據,超過1分鍾。反復測了幾次,結果相差不大,假如將SaveChanges操作包裹在TransactionScope中(純粹為了測試,看是不是更耗時),結果沒什么變化。
2、.NET4.5,EF4.4
聽說只要升級到.NET4.5,EF就會有性能上的提升,因為EF用到了.NET框架的某些類庫,隨着這些類庫的升級,EF也提高了性能。so,干巴爹。結果:
(這圖可以不用貼,因為和上圖沒什么兩樣)
3、.NET4.5,EF5.0
微軟跟我說5.0有67%的性能提升(有人說只是提高了查詢性能,這里側重調侃),我想這么大公司會亂說?於是卸載了4.4,安裝了5.0(貌似NuGet里,會根據你的.NET版本安裝相應版本的EF;但是你.NET升級后,EF不會跟着升級,也不能直接更新)。重新啟動測試,結果:
(這圖可以不用貼,因為和上圖沒什么兩樣)
4、其實么,關鍵不是在數據庫插入階段,而是在代碼層的Add階段,so,我並不認為和ADO.NET相比較有什么意義,因為ADO.NET沒有上下文,而低效點就是Add到上下文的過程(博主在這里明確表示,以下代碼是為了比較SaveChanges方法和ADO.NET的執行效率,而非整個插入階段的效率)。有朋友評論說我Add的方式錯了,不知道應該怎么寫,請教。
無論如何,貼代碼吧:
1 public void ExecRealTimeRunByADO(List<RealTimeStocks> realTimeData) 2 { 3 string insertText = @"insert [dbo].[RealTimeStocks]([Hqgpdm], [Hqzrsp], [Hqjrkp], [Hqzjcj], [Hqcjsl], [Hqdqcjsl], 4 [Hqcjje], [Hqdqcjje], [Hqcjbs], [Hqzgcj], [Hqzdcj], [Hqsyl1], [Hqsyl2], [Hqjgsd1], [Hqjgsd2], [Hqhycc], [Hqsjw5], [Hqssl5], 5 [Hqsjw4], [Hqssl4], [Hqsjw3], [Hqssl3], [Hqsjw2], [Hqssl2], [Hqsjw1], [Hqssl1], [Hqbjw1], [Hqbsl1], [Hqbjw2], [Hqbsl2], 6 [Hqbjw3], [Hqbsl3], [Hqbjw4], [Hqbsl4], [Hqbjw5], [Hqbsl5], [HQTime], [UpdateTime], [ExponentRiseDown], [RiseDownPercent], 7 [ExponentSwing], [TimeID], [RiseNum], [DownNum], [EqualNum], [DataSource], [IsDeleted], [AddTime], [SMGUID]) 8 values ('{0}', {1}, {2}, {3}, {4}, null, {5}, null, null, {6}, {7}, {8}, null, {9}, {10}, null, {11}, {12}, {13}, {14}, {15}, 9 {16}, {17}, {18}, null, {19}, null, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, '{29}', '{30}', {31}, {32}, {33}, {34}, 10 {35}, {36}, {37}, null, {38}, '{39}', '{40}')"; 11 using (var context = new StockDataEntities()) 12 { 13 context.Database.ExecuteSqlCommand("delete from RealTimeStocks"); 14 15 var now2 = DateTime.Now.TimeOfDay; 16 Console.WriteLine(string.Format("{0}開始執行Insert操作", now2)); 17 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TimeSpan(0, 2, 0))) 18 { 19 try 20 { 21 foreach (var data in realTimeData) 22 { 23 string cmd = string.Format(insertText, data.Hqgpdm, data.Hqzrsp, data.Hqjrkp, data.Hqzjcj, data.Hqcjsl, 24 data.Hqcjje, data.Hqzgcj, data.Hqzdcj, data.Hqsyl1, data.Hqjgsd1, data.Hqjgsd2, data.Hqsjw5, data.Hqssl5, 25 data.Hqsjw4, data.Hqssl4, data.Hqsjw3, data.Hqssl3, data.Hqsjw2, data.Hqssl2, data.Hqssl1, data.Hqbsl1, data.Hqbjw2, data.Hqbsl2, 26 data.Hqbjw3, data.Hqbsl3, data.Hqbjw4, data.Hqbsl4, data.Hqbjw5, data.Hqbsl5, data.HQTime, data.UpdateTime, data.ExponentRiseDown, data.RiseDownPercent, 27 data.ExponentSwing, data.TimeID, data.RiseNum, data.DownNum, data.EqualNum, data.IsDeleted ? 1 : 0, data.AddTime, data.SMGUID); 28 context.Database.ExecuteSqlCommand(cmd); 29 } 30 scope.Complete(); 31 } 32 catch 33 { 34 Console.WriteLine("出錯"); 35 return; 36 } 37 38 var now3 = DateTime.Now.TimeOfDay; 39 Console.WriteLine(string.Format("{0}Insert完畢,耗時{1}", now3, now3 - now2)); 40 } 41 } 42 }
Insert語句我是直接拷貝EF動態生成的語句,稍微修改了下。使用的雖然不是正宗的ADO.NET,但是也差不多,你別跟我說EF的ExecuteSqlCommand用的是另一套東西。
結果:
不言自明(看來還有些人不明白,博主想說:SaveChanges並沒有多少性能損失)。
結論:坐等10.0版本!or context.Configuration.AutoDetectChangesEnabled = false!(thx to eflay && flytothemoon)
轉載請注明本文出處:http://www.cnblogs.com/newton/archive/2013/06/06/3120497.html