关于EF中批量添加的个人探索
Posted 咕咚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于EF中批量添加的个人探索相关的知识,希望对你有一定的参考价值。
实际的测试代码和数据记录,还有最终的总结都在下面:
/// <summary> /// 这种做法,不用了说了,每次遍历都会打开一次db链接,然后执行insert操作; /// </summary> static void CreateBluckInsertData0() { using (var context = new SiteDbContext()) { List<Role> list = new List<Role>(); var count = 1000; for (int i = 0; i < count; i++) { var entity = new Role() { RoleName = "普通员工" + i }; context.Roles.Add(entity); context.SaveChanges(); } } } /// <summary> /// 初看,觉得,这样做( context.SaveChanges()方在for循环外面)挺好的, /// 实际跟踪slq发现,还是执行了1000的插入操作,只不过没有在for循环里面; /// 而是在我们的for循环外;总结:不可取; /// </summary> static void CreateBluckInsertData1() { using (var context = new SiteDbContext()) { List<Role> list = new List<Role>(); var count = 1000; for (int i = 0; i < count; i++) { var entity = new Role() { RoleName = "普通员工" + i }; context.Roles.Add(entity); } context.SaveChanges();//会增加与数据库的交互次数 //EF的一个上下文在提交时会打开一个数据连接,然后把转换成的SQL语句一条一条的发到数据库端,然后去提交 } } /// <summary> /// 拼接字符串;组装后一次性操作;确定,传递的字符数量就会很多;网络压力增加;但不管怎样都比上面两张好; /// </summary> static void CreateBluckInsertData2() { Stopwatch watch = Stopwatch.StartNew(); var count = 92000; using (var context = new SiteDbContext()) { var bluckString = new StringBuilder(); for (int i = 0; i < count; i++) { bluckString.Append("INSERT INTO ROLES(RoleName) VALUES(\'"); bluckString.Append("WORKER"); bluckString.Append(i); bluckString.Append("\');"); } var result = bluckString.ToString(); Console.WriteLine(string.Format("拼接字符串花费的时间:{0} milliseconds.",watch.ElapsedMilliseconds)); //然后这里再来一次性批量的进行插入操作; watch.Restart(); context.Database.ExecuteSqlCommand(result); //这样做的好处就是,可以一次性,全部插入,缺点就是;发送大量的insert 文本信息; //1000 customers are created, cost 1777 milliseconds. //5000 customers are created, cost 1906 milliseconds. //9000 customers are created, cost 2354 milliseconds. //9000 customers are created, cost 2023 milliseconds. //这样的计算比较草率; } watch.Stop(); Console.WriteLine(string.Format("{0} customers are created, cost {1} milliseconds.", count.ToString(), watch.ElapsedMilliseconds)); //结果:拼接字符串花费的时间:97 milliseconds. //12000 customers are created, cost 2028 milliseconds. //拼接字符串花费的时间:99 milliseconds. //22000 customers are created, cost 2336 milliseconds. //拼接字符串花费的时间:118 milliseconds. //92000 customers are created, cost 6553 milliseconds. } /// <summary> /// 这里我们使用第三种方法; /// 网上提供的插件的方法; /// 你以为,插件的方法,就是单纯的封装上面的操作?太年轻了,俺都没监测到一条insert 语句;那么它是怎么做的呢? /// /// </summary> static void CreateBluckInsertData3() { Stopwatch watch = Stopwatch.StartNew(); var count = 92000; using (var context = new SiteDbContext()) { List<Role> list = new List<Role>(); for (int i = 0; i < count; i++) { var entity = new Role() { RoleName = "普通员工" + i }; list.Add(entity); //context.Roles.Add(entity); //fuck stupid; } Console.WriteLine(string.Format("拼接对象花费的时间:{0} milliseconds.", watch.ElapsedMilliseconds)); //然后这里再来一次性批量的进行插入操作; watch.Restart(); context.BulkInsert(list); context.BulkSaveChanges(); } watch.Stop(); Console.WriteLine(string.Format("{0} customers are created, cost {1} milliseconds.", count.ToString(), watch.ElapsedMilliseconds)); //1000 customers are created, cost 2865 milliseconds //5000 customers are created, cost 18207 milliseconds. //9000 customers are created, cost 51134 milliseconds. (发现是方法用错了,窝草).不要把 context.Roles.Add(entity); 添加在for循环中; //然后结果是这样的:9000 customers are created, cost 2320 milliseconds. //效率明显比上面的方法提高了很多; //由此可见,我们的批量,效果操作,并不由之前那种方法高呢; //拼接对象花费的时间:102 milliseconds. //12000 customers are created, cost 2238 milliseconds. //拼接对象花费的时间:101 milliseconds. //22000 customers are created, cost 2258 milliseconds. ///拼接对象花费的时间:123 milliseconds. // 92000 customers are created, cost 3054 milliseconds. //这种方式的优势就不断体现出来了; //总结,凭借,字段串额效果,要比拼接对象集合的效率要高一些; //然后,就是我们的 } //可能涉及到一些批量数据迁移的时候; ///总结: public void Info() { //凭借字段串的效率比拼接对象List的效率要高一些; //方式2的缺点在于,要传送大量的sql语句到我们的db中去执行, //方式3的实现方式和方式一有着本质的区别;是通过; //是数据小于五万条的时候,方式2的效率高,随着数据量的增加;方式3的优势就体现出来了 //如果实际的开发中遇到大数据的批量操作;建议还是是用插件方式,就是我们的的方式3; //ps 操作中犯了一个错,是list.add(entity) 而不是 context.Roles.Add(entity); //fuck stupid; } static void Main(string[] args) { HibernatingRhinos.Profiler.Appender.EntityFramework.EntityFrameworkProfiler.Initialize(); //CreateBluckInsertData1(); //CreateDB(); //CreateBluckInsertData3(); // CreateBluckInsertData2(); Console.ReadLine(); }
当然,这里还有我们的另外一种做法;
SqlBulkCopy 接口描述
Microsoft SQL Server 提供一个称为 bcp 的流行的命令提示符实用工具,用于将数据从一个表移动到另一个表(表既可以在同一个服务器上,也可以在不同服务器上)。 SqlBulkCopy 类允许编写提供类似功能的托管代码解决方案
似乎这种效率更高一些(不过,俺没有去测)
阅读资料后,发现,z的扩展插件使用的就是我们的sqlbulkcopy接口滴呀;
大致的流程如下:
- 在SQL Server中创建一张临时表;
- 使用.NET SqlBulkCopy将数据批量插入临时表;
- 在临时表和目标表之间执行一条SQL语句;
- 从SQL Server删除临时表。
参考文献:
http://www.cnblogs.com/gaochundong/p/entity_framework_bulk_insert_extension.html
这里有一偏使用心得:
https://www.cnblogs.com/mobydick/archive/2011/08/28/2155983.html
还有这个:
https://www.cnblogs.com/zfanlong1314/archive/2013/02/05/2892998.html
以上是关于关于EF中批量添加的个人探索的主要内容,如果未能解决你的问题,请参考以下文章