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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 扩展下载地址:http://efbulkinsert.codeplex.com/注意同时安装依赖项目,不然会报错,还有,程序中有同一个dll的其他版本,那就可能一次安装不上,得一个一个安装依赖的dllInstall-Package EntityFramework.

扩展下载地址: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数据,速度上没有什么改进的


//批量插入测试代码


            StringBuilder sb = new StringBuilder();
            FinanceReceipts model = ReceiptsRepository.Entities.Include(o => o.FinanceReceiptDetail).Include(o => o.FinanceBillLog).First(o => o.ReceiptId == 214539);
            int createCount = 10000;
            model.ReceiptId = 0;
            model.ReceiptStatus = -1;
            model.ReceiptNo = "";
            model.FinanceBillLog.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);
            model.FinanceReceiptDetail.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);
            model.ActualCreateTime = DateTime.Now;
            List<FinanceReceipts> entities = new List<FinanceReceipts>();
            for (int i = 0; i < createCount; i++)
            {
                FinanceReceipts temp = model.DeepCopy();
                model.ReceiptNo = "ef" + i;
                entities.Add(temp);
            }
            Stopwatch sw = new Stopwatch();
            sw.Start();
            ReceiptsRepository.Insert(entities);
            sw.Stop();
            sb.AppendFormat("EF插入耗时:{0}\r\n", sw.ElapsedMilliseconds);

            model.ActualCreateTime = DateTime.Now;
            List<FinanceReceipts> entities2 = new List<FinanceReceipts>();
            for (int i = 0; i < createCount; i++)
            {
                FinanceReceipts temp = model;
                model.ReceiptNo = "bi" + i;
                entities2.Add(temp);
            }
            sw.Restart();
            var ctx = (this.UnitOfWork as UnitOfWorkContextBase).DbContext;
                using (var tran = EfDbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    // some stuff in dbcontext

                    ctx.BulkInsert(entities2, tran.UnderlyingTransaction);

                    ctx.SaveChanges();
                    tran.Complete();
                }
            sw.Stop();
            sb.AppendFormat("BulkInsert插入耗时:{0}\r\n", sw.ElapsedMilliseconds);
            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次,单据明细有多少条就传多少次,单据日志有多少条就传多少次)


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



            StringBuilder sb = new StringBuilder();
            FinanceReceipts model = ReceiptsRepository.Entities.Include(o => o.FinanceReceiptDetail).Include(o => o.FinanceBillLog).First(o => o.ReceiptId == 214539);
            int createCount = 10;
            model.ReceiptId = 0;
            model.ReceiptStatus = -1;
            model.ReceiptNo = "";
            model.FinanceBillLog.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);
            model.FinanceReceiptDetail.OpenSafe().ToList().ForEach(m => m.ReceiptId = 0);
            model.ActualCreateTime = DateTime.Now;
            List<FinanceReceipts> entities = new List<FinanceReceipts>();
            for (int i = 0; i < createCount; i++)
            {
                FinanceReceipts temp = model.DeepCopy();
                model.ReceiptNo = "ef" + i;
                entities.Add(temp);
            }
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < createCount; i++)
            {
                ReceiptsRepository.Insert(entities[i]);
            }
            sw.Stop();
            sb.AppendFormat("EF插入耗时:{0}\r\n", sw.ElapsedMilliseconds);

            model.ActualCreateTime = DateTime.Now;
            List<FinanceReceipts> entities2 = new List<FinanceReceipts>();
            for (int i = 0; i < createCount; i++)
            {
                FinanceReceipts temp = model;
                model.ReceiptNo = "bi" + i;
                entities2.Add(temp);
            }
            sw.Restart();
            var ctx = (this.UnitOfWork as UnitOfWorkContextBase).DbContext;
            for (int i = 0; i < createCount; i++)
            {
                using (var transactionScope = new TransactionScope())
                {
                    // some stuff in dbcontext

                    ctx.BulkInsert(new List<FinanceReceipts>(){entities2[i]});

                    ctx.SaveChanges();
                    transactionScope.Complete();
                }
            }

            sw.Stop();
            sb.AppendFormat("BulkInsert插入耗时:{0}\r\n", sw.ElapsedMilliseconds);
            string ret = sb.ToString();






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

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

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

这个是比较麻烦的地方。


最新:

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

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


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
SQL 关系型数据库 MySQL
ThinkPHP6 连接使用数据库,增删改查,find,select,save,insert,insertAll,insertGetId,delete,update方法的用法
本文介绍了在ThinkPHP6框架中如何连接和使用数据库进行增删改查操作。内容包括配置数据库连接信息、使用Db类进行原生MySQL查询、find方法查询单个数据、select方法查询数据集、save方法添加数据、insertAll方法批量添加数据、insertGetId方法添加数据并返回自增主键、delete方法删除数据和update方法更新数据。此外,还说明了如何通过数据库配置文件进行数据库连接信息的配置,并强调了在使用Db类时需要先将其引入。
ThinkPHP6 连接使用数据库,增删改查,find,select,save,insert,insertAll,insertGetId,delete,update方法的用法
|
SQL
EF框架-SQL语句 查询与修改
EF框架-SQL语句 查询与修改
56 0
|
SQL 开发框架 .NET
ef linq方式插入+sql操作数据注意事项
ef linq方式插入+sql操作数据注意事项
90 0
|
SQL 数据库
EF框架-SQL语句 增加 删除
EF框架-SQL语句 增加 删除
88 0
|
数据建模 数据库 索引
EF CodeFirst增删改查之‘CRUD’
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。    十年河东十年河西,莫欺少年穷    学无止境,精益求精    本篇旨在学习EF增删改查四大操作    上一节讲述了EF CodeFirst 创建数据库,本节继续引用上一节的相关类学习EF的CRU...
1219 0
|
Web App开发 数据库 容器
EntityFramework中常用的数据删除方式速度对比
EntityFramework中常用的数据删除方式速度对比http://www.bieryun.com/3375.html 最近在学EF,目前了解到删除操作有三种方式, 第一,官方推荐的先查询数据,再根据查询的对象,删除对象。
1415 0
|
SQL
SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码)
原文:SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码) SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码) 概述: 表由行和列组成,每个表都必须有个表名。
1278 0
|
SQL 数据库 索引
SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)
原文:SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码) SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)-DML 1、SQL INSERT INTO 语句(在表中插入) INSERT INTO 语句用于向表中插入新记录。
1094 0
下一篇
无影云桌面