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数据,速度上没有什么改进的

//批量插入测试代码

            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); 

时间: 2024-07-28 14:49:20

EntityFramework.BulkInsert扩展插入数据和EF本身插入数据比较的相关文章

谷歌扩展 js-求助:谷歌扩展 如何使插入页面的内容脚本里面数据在刷新之后不丢失

问题描述 求助:谷歌扩展 如何使插入页面的内容脚本里面数据在刷新之后不丢失 学了几天js,自己编了一个扩展,有个内容脚本,每次刷新页面的时候都会重新插入,导致一些赋值的数据丢失.请问怎么解决? ps:也不知道问的地方对不对 第一次提问好紧张啊 怎么才能装作经常发帖的样子呢 解决方案 可以把这些数据存到chrome的数据库中,这样下次加载的时候读取.

sql-如何在vsGridView中插入一张表的部分数据?

问题描述 如何在vsGridView中插入一张表的部分数据? GridView中: 我用的是SQL2008,现在我想只要这个表里商品名称含有"包"字的数据,滤掉其他无关的过滤掉,怎么实现啊,新手求大神解答啊!!!!!!!!! 解决方案 DataTable或者SQL中过滤,将结果返回数据源,不需要改GridView的代码. 如果用sql就是 select * from 你的表 where 字段 like '%包%'

sql实现插入数据主键重复或数据已经存在,则更新这条数据

在做数据库开发的时候,经常会遇到这样的一种情景: 当一条数据不存在的时候,插入这条数据,如果这条数据的主键已经在数据库中存在,那么更新这条数据. 你们一般怎么做呢?先根据主键查询数据,然后判断是否存在数据,如果存在数据,则update字段,否则insert数据. 这样做的弊端就是需要两次连接数据库服务器,然后利用高级语言来判断是否存在的逻辑. 下面教你一条SQL语句,教你解决这类问题! 例如数据表weixin_user的表结构如下所示:(博客转移,图片丢失) $sql = "insert int

批量将数据表中原有的数据稍微变更再插入本表中

问题描述 批量将数据表中原有的数据稍微变更再插入本表中 表A中有以下字段,A_id,A_user_id,A_func_code,现在表A中有以下数据 A_id A_user_id A_func_code 1 张三 111 2 张三 222 3 张三 333 现在要表A中再插入数据,数据要求 要求为A_user_id等于张三的再插入时张三变更为李四 A_id为 sequence,大致描述如此,实际数据中不止这几个字段,数据也不止三条 等于说就是把原表中的A_user_id的数据再插入到本表中,只是

javascript代码如何向数据库中插入精确到时分秒的date数据?

问题描述 javascript代码如何向数据库中插入精确到时分秒的date数据? 插入到数据库中发现只有年月日..时分秒为0. 有没有办法插入精确到时分秒的日期数据?? 解决方案 从后台给时间呗.干嘛要在前台给 解决方案二: 可以参考这个javascript datetime时间操作函数http://www.111cn.net/wy/js-ajax/39366.htm 解决方案三: 你后台的是什么数据库,数据库字段是什么?不行的话,可以使用timestamp时间戳,转成EPOCH来存储. 解决方

在PB窗体中插入数据显示“插入成功”但查询后没有数据

问题描述 在PB窗体中插入数据显示"插入成功"但查询后没有数据 string r_dept_code, r_date1,r_date2,r_sp_code dec r_amount1,r_amount2,r_amount3 integer r_row,r_rowcount r_row=dw_1.getrow() r_rowcount=dw_1.rowcount() for r_row=1 to r_rowcount r_dept_code= dw_1.getitemstring(r_r

数据库查询不到数据,完全没有数据,如果给它造一个假数据,但是不能插入到数据里面

问题描述 数据库查询不到数据,完全没有数据,如果给它造一个假数据,但是不能插入到数据里面 数据库查询不到数据,完全没有数据.如何给它造一个假数据,但是不能插入到数据里面. 求指教 解决方案 执行SQL插入数据报什么错了吗?检查你插入的数据格式是否跟表结构一致. 解决方案二: 数据库里面造数据 解决方案三: 这种时候需要把你的错误贴出来,不然很难回答 解决方案四: 1.你的查询语句有问题2.你插入的假数据违反了某种约束或者格式有问题如果还是不能解决,建议 楼主把错误拿出来看看

c++ 大数据 hash_map-hash_map,unordered_map以string作为键,插入一千多万条数据后崩溃

问题描述 hash_map,unordered_map以string作为键,插入一千多万条数据后崩溃 代码: #include "stdafx.h" #include"iostream" #include #include #include #include #include using namespace std; typedef hash_map myMap; void CharsAllSequen(myMap &resultMap, string &a

spring mvc-mybatis插入唯一字段(编号)不重复数据!

问题描述 mybatis插入唯一字段(编号)不重复数据! mybatis+spring mvc做一下简单的CRUD, 如果插入的数据的编号已经存在,插入失败,该怎么写,说具体点,例如说是在映射文件里面写还是另外在service层写java类! 解决方案 不用你编写插入失败.唯一字段你插入数据库时自己会检验 解决方案二: 编号你为什么不在建立数据库的时候让他自增长呢?现在编号是程序决定的么,如果是,你可以用这个编号查一下数据库如果有,就换一个......或者在数据库创建序列,好像oracle那种的