写在开头:本文的评论者大多认为我这个测试不对,但是哪里不对没有谁给出一个明确的回复;对于若干纯粹谩骂的评论(似乎我说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