EntityFramework.BulkInsert扩展插入数据和EF本身插入数据比较

扩展下载地址:http://efbulkinsert.codeplex.com/

注意同时安装依赖项目,不然会报错,还有,程序中有同一个dll的其他版本,那就可能一次安装不上,得一个一个安装依赖的dll

Install-Package EntityFramework.MappingAPI -Version 6.0.0.7

Install-Package EntityFramework.BulkInsert-ef6



EntityFramework.BulkInsert插入数据和EF比较


初步猜测,它应该只是把多个sql合成一个,不管怎么优化,总该最后生成的是sql。
例如:20条数据,ef调试时看到的是一次连接,20次执行sql,这个批量,估计是一次连接,20个sql组合放到一个字符串提交,这样能减少时间。
再优化也不可能把sql给减少,同一sql在数据库中执行时间也不是EF能减少的。


实测(222数据库,表FinanceReceipts):
用Stopwatch监视执行时间(单位毫秒)



一次插入200条单据测试


EF插入耗时:11,086
BulkInsert插入耗时:740


一次插入10000条单据测试


EF插入耗时:510,640
BulkInsert插入耗时:3,200


通过看代码,和猜测的实现方式差不多,不过,代码中有表映射,为什么有这些功能?


因为 Insert 比数据库自带的 SqlBulkCopy 功能慢,

EntityFramework.BulkInsert扩展在优化语句传输次数的同时,也采用了速度更快的 SqlBulkCopy 去将数据插入数据库,所以在插入大数据量时,比起EF本身的插入数据,可以说快得“离谱”。


不过,利用这个SqlBulkCopy快速插入数据,也就只能在插入上有改进,对于Update,Delete数据,速度上没有什么改进的


//批量插入测试代码


[csharp]  view plain  copy
 print ?
  1. StringBuilder sb = new StringBuilder();  
  2. FinanceReceipts model = ReceiptsRepository.Entities.Include(o => o.FinanceReceiptDetail).Include(o => o.FinanceBillLog).First(o => o.ReceiptId == 214539);  
  3. int createCount = 10000;  
  4. model.ReceiptId = 0;  
  5. model.ReceiptStatus = -1;  
  6. model.ReceiptNo = "";  
  7. model.FinanceBillLog.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);  
  8. model.FinanceReceiptDetail.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);  
  9. model.ActualCreateTime = DateTime.Now;  
  10. List<FinanceReceipts> entities = new List<FinanceReceipts>();  
  11. for (int i = 0; i < createCount; i++)  
  12. {  
  13.     FinanceReceipts temp = model.DeepCopy();  
  14.     model.ReceiptNo = "ef" + i;  
  15.     entities.Add(temp);  
  16. }  
  17. Stopwatch sw = new Stopwatch();  
  18. sw.Start();  
  19. ReceiptsRepository.Insert(entities);  
  20. sw.Stop();  
  21. sb.AppendFormat("EF插入耗时:{0}\r\n", sw.ElapsedMilliseconds);  
  22.   
  23. model.ActualCreateTime = DateTime.Now;  
  24. List<FinanceReceipts> entities2 = new List<FinanceReceipts>();  
  25. for (int i = 0; i < createCount; i++)  
  26. {  
  27.     FinanceReceipts temp = model;  
  28.     model.ReceiptNo = "bi" + i;  
  29.     entities2.Add(temp);  
  30. }  
  31. sw.Restart();  
  32. var ctx = (this.UnitOfWork as UnitOfWorkContextBase).DbContext;  
  33.     using (var transactionScope = new TransactionScope())  
  34.     {  
  35.         // some stuff in dbcontext  
  36.   
  37.         ctx.BulkInsert(entities2);  
  38.   
  39.         ctx.SaveChanges();  
  40.         transactionScope.Complete();  
  41.     }  
  42. sw.Stop();  
  43. sb.AppendFormat("BulkInsert插入耗时:{0}\r\n", sw.ElapsedMilliseconds);  
  44. string ret = sb.ToString();  





插入100条,每次插入一条,循环插入测试


第1次:
EF插入耗时:9006
BulkInsert插入耗时:4173


第2次:
EF插入耗时:8738
BulkInsert插入耗时:3806


第3次:
EF插入耗时:8784
BulkInsert插入耗时:3727


BulkInsert还是比EF本身插入数据稍微快一点,总的来说:

BulkInsert 大致是只传一次sql语句,并用SqlBulkCopy快速插入(单据+单据明细+单据日志);

而EF是每个实体一条普通的Insert插入(单据1次,单据明细有多少条就传多少次,单据日志有多少条就传多少次)


//每次插入一条,循环插入的测试代码



[csharp]  view plain  copy
 print ?
  1. StringBuilder sb = new StringBuilder();  
  2. FinanceReceipts model = ReceiptsRepository.Entities.Include(o => o.FinanceReceiptDetail).Include(o => o.FinanceBillLog).First(o => o.ReceiptId == 214539);  
  3. int createCount = 10;  
  4. model.ReceiptId = 0;  
  5. model.ReceiptStatus = -1;  
  6. model.ReceiptNo = "";  
  7. model.FinanceBillLog.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);  
  8. model.FinanceReceiptDetail.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);  
  9. model.ActualCreateTime = DateTime.Now;  
  10. List<FinanceReceipts> entities = new List<FinanceReceipts>();  
  11. for (int i = 0; i < createCount; i++)  
  12. {  
  13.     FinanceReceipts temp = model.DeepCopy();  
  14.     model.ReceiptNo = "ef" + i;  
  15.     entities.Add(temp);  
  16. }  
  17. Stopwatch sw = new Stopwatch();  
  18. sw.Start();  
  19. for (int i = 0; i < createCount; i++)  
  20. {  
  21.     ReceiptsRepository.Insert(entities[i]);  
  22. }  
  23. sw.Stop();  
  24. sb.AppendFormat("EF插入耗时:{0}\r\n", sw.ElapsedMilliseconds);  
  25.   
  26. model.ActualCreateTime = DateTime.Now;  
  27. List<FinanceReceipts> entities2 = new List<FinanceReceipts>();  
  28. for (int i = 0; i < createCount; i++)  
  29. {  
  30.     FinanceReceipts temp = model;  
  31.     model.ReceiptNo = "bi" + i;  
  32.     entities2.Add(temp);  
  33. }  
  34. sw.Restart();  
  35. var ctx = (this.UnitOfWork as UnitOfWorkContextBase).DbContext;  
  36. for (int i = 0; i < createCount; i++)  
  37. {  
  38.     using (var transactionScope = new TransactionScope())  
  39.     {  
  40.         // some stuff in dbcontext  
  41.   
  42.         ctx.BulkInsert(new List<FinanceReceipts>(){entities2[i]});  
  43.   
  44.         ctx.SaveChanges();  
  45.         transactionScope.Complete();  
  46.     }  
  47. }  
  48.   
  49. sw.Stop();  
  50. sb.AppendFormat("BulkInsert插入耗时:{0}\r\n", sw.ElapsedMilliseconds);  
  51. string ret = sb.ToString();  






附:EntityFramework.BulkInsert 突出点是快,但是实际应用上,有诸多不便,比如:

我们用EF插入,自动增长键会带回数据库中的值,但是BulkInsert 不会;

外键关联表的数据,比如:日志、详情,用EF时,如果有数据会自动插入;用BulkInsert 不会插入,只会插入主表的数据,还要自己根据业务,查询出主表的主键然后赋值给 日志、详情的外键字段,然后我们才可以将这些数据再次插入数据库。

这个是比较麻烦的地方。


最新:

虽然在事务using语句中,但 ctx.BulkInsert(entityArray); 不会回滚事务 ,

加上参数,才能回滚:ctx.BulkInsert(entityArray, tran.UnderlyingTransaction); 


你可能感兴趣的:(EntityFramework.BulkInsert扩展插入数据和EF本身插入数据比较)