C# EF 批量操做

背景

源代码下载地址在最后
知识要求:ef code first
ef 批量操做是最近遇到的一个新问题,ef这个orm为咱们解决了大量的curd操做,可是,对于批量操做,其性能一直没有很好的方案,无论是 foreach 方式,仍是 addorupdate(这个扩展内部实现原理仍是一个一个add),当数据量很大的时候,其性能简直是不能容忍,差很少1万多的数据,须要等半个小时左右!
因而开始着手寻找一个可使用ef进行批量操做的的类库,开始用的是 zzz projects 的类库,可是其免费版,只有更新和删除,没有插入,使用非免费版,若是时间过时,会致使程序出问题(血的教训),因而有找啊找,皇天不负有心人,这里须要吐槽下百度,在百度上,基本上搜索到的答案,均是 zzz projects 的类库 或者 ef extend ,后者也没有 批量插入。
不得已,咱们翻出去看看,不会FQ的程序员不是好的搬砖工,在google一搜,搜到了很漂亮,很好用的一个类库:EntityFramework.Utilities,地址:https://github.com/MikaelEliasson/EntityFramework.Utilities, 关键这货仍是免费开源的……
咱们的故事就从这里开始!javascript


建立项目

使用vs工具,建立一个控制台程序,并引入nuget 包:php

 
图片.png

实体类以下:java

using System.ComponentModel.DataAnnotations; public class TestEntity { [Key] public int Id { get; set; } public int RandomVlue { get; set; } } 

上下文以下:git

public class BatchDemoContext : DbContext { public BatchDemoContext() : base("Default") { } public IDbSet<TestEntity> TestEntities { get; set; } } 

有了以上代码,咱们就可使用code first 命令建立数据库,前提是,在配置文件里,添加了数据库链接字符串程序员


建立数据源

有了以上内容,接下来,咱们须要生产多条数据,好比,生产100000条数据,具体的代码以下github

/// <summary> /// 产生须要生产的数据 /// </summary> /// <returns></returns> private static IEnumerable<TestEntity> GetInsertDatas() { // 线程安全的list ConcurrentBag<TestEntity> datas=new ConcurrentBag<TestEntity>(); Parallel.For(0, 100000, (index, state) => { Random rand = new Random(); var newData = new TestEntity { RandomVlue = rand.Next(1, 100) }; datas.Add(newData); }); return datas; } 

为了生产数据快,使用了Parallel,不懂的能够自行 google,若是不能FQ你就bing一下,或者是看《 C# 高级编程》 关于异步 的这章,这里,只须要知道,生产100000条数据,用parallel会很快的产生数据便可!sql


批量插入

咱们获取了数据源,那么,如何对这些数据进行插入呢,看以下的测试代码:数据库

/// <summary> /// 批量插入 /// </summary> private static void BatchInster() { var datas = GetInsertDatas(); var testEntities = datas as IList<TestEntity> ?? datas.ToList(); Stopwatch watch =new Stopwatch(); Console.WriteLine("开始插入计时,总共数据:{0}条",testEntities.Count()); watch.Start(); using (var context=new BatchDemoContext()) { EFBatchOperation.For(context,context.TestEntities) .InsertAll(testEntities); } watch.Stop(); Console.WriteLine("结束插入计时,工用时:{0}ms",watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("数据库总共数据:{0}条",count); var minId = context.TestEntities.Min(c => c.Id); // 随机取十条数据进行验证 for (int i = 1; i <= 10; i++) { Random rand = new Random(); var id = rand.Next(minId, minId+ 100000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("插入的数据 id:{0} randomvalue:{1}",testdata.Id,testdata.RandomVlue); } } Console.WriteLine("-----------------华丽的分割线 插入-------------------------"); } 

用 Stopwatch 监测 插入所执行的时间
经过使用代码编程

using (var context=new BatchDemoContext()) { EFBatchOperation.For(context,context.TestEntities) .InsertAll(testEntities); } 

便可实现批量插入,而后,针对这10调数据,随机随十条进行验证,咱们在生产数据的时候,默认的随机值不会超过100,若是这10条随机值,都是小于100的,能够认为插入成功。
使用
var minId = context.TestEntities.Min(c => c.Id);
是为了保证,id条件最小,否则第二次运行程序,会出错,由于id是自增的,删除数据,id也不会从1开始!
插入的运行结果以下:安全

 
图片.png

经过上图能够看出
须要插入的数据有 100000条,插入仅用了 3070ms,这比原生态的ef要快了不知道多少倍……
经过插入数据的id和randomvalue,能够看出,咱们的数据,也的确是正确的插入到了数据库!


批量更新

经过上面的步骤,咱们数据库里已经有100000条数据里,如今,咱们将数据库里的数据,randomvalue 所有设置为 1000,因而咱们须要,获取所有数据,而后并行运算,改randomvalue的值为100000,在而后批量更新修改后的数据
代码以下

/// <summary> /// 批量更新 /// </summary> private static void BatchUpdate() { IEnumerable<TestEntity> toUpdates=new List<TestEntity>(); // 获取全部数据 using (var context = new BatchDemoContext()) { toUpdates = context.TestEntities.ToList(); } // 全部的值 都为 1000 Parallel.ForEach(toUpdates, (entity, state) => { entity.RandomVlue = 1000; }); Stopwatch watch = new Stopwatch(); Console.WriteLine("开始更新计时,总共数据:{0}条", toUpdates.Count()); watch.Start(); using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities).UpdateAll(toUpdates, x => x.ColumnsToUpdate(c => c.RandomVlue)); } watch.Stop(); Console.WriteLine("结束更新计时,工用时:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("数据库总共数据:{0}条", count); var minId = context.TestEntities.Min(c => c.Id); // 随机取十条数据进行验证 for (int i = 1; i <= 10; i++) { Random Rand = new Random(); var id = Rand.Next(minId, minId+100000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("更新的数据 id:{0} randomvalue:{1}", testdata.Id, testdata.RandomVlue); } } Console.WriteLine("-----------------华丽的分割线 更新-------------------------"); } 

经过

using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities).UpdateAll(toUpdates, x => x.ColumnsToUpdate(c => c.RandomVlue)); } 

这条语句,告诉ef,须要更新哪一个集合的randomvalue!
而后随机取10条数据,发现,randomvalue的值所有都是 1000,说明咱们批量更新成功!
运行结果以下:

 
图片.png

查询更新

什么是查询更新呢?就是当咱们知足什么条件的时候,对属性进行什么操做,相似于 update table set col=value where id=1 这样的 sql 语句,和批量更新有什么区别的?我这边的批量更新,是指从数据库中加载的多个实体到内存,在内存中改变了属性值,在将这一批数据,更新到数据库,而查询更新,无需查询到内存!
这里,咱们将id大于等于 minid+10000的数据和 id 小于等于 minid+50000的数据进行改值,修改ran 的值为 500,
代码以下

/// <summary> /// 将id >= 1w 小于 5w 的随机值等于 500 /// </summary> private static void BatchUpdateQuery() { Stopwatch watch = new Stopwatch(); Console.WriteLine("开始查询更新计时"); watch.Start(); using (var context = new BatchDemoContext()) { var minId = context.TestEntities.Min(c => c.Id); EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>= minId+10000 && c.Id<= minId+50000) .Update(c=>c.RandomVlue,rv=>500); } watch.Stop(); Console.WriteLine("结束查询更新计时,工用时:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("数据库总共数据:{0}条", count); var minId = context.TestEntities.Min(c => c.Id); // 随机取十条数据进行验证 for (int i = 1; i <= 10; i++) { Random rand = new Random(); var id = rand.Next(minId+10000, minId+ 50000); var testdata = context.TestEntities.FirstOrDefault(c => c.Id == id); Console.WriteLine("查询更新的数据 id:{0} randomvalue:{1}", testdata.Id, testdata.RandomVlue); } } Console.WriteLine("-----------------华丽的分割线 查询更新-------------------------"); } 

经过代码

var minId = context.TestEntities.Min(c => c.Id); EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>= minId+10000 && c.Id<= minId+50000) .Update(c=>c.RandomVlue,rv=>500); 

进行查询更新
而后取十条数据进行验证,具体的运行结果以下:

 
图片.png

经过结果,咱们能够看出,查询更新也执行成功了


批量删除

相似的sql语句是 : delete from table where id=1
在ef里,删除只能是先获取,在remove,咱们如何使用efUtilities进行批量删除呢?
看代码

/// <summary> /// 删除全部数据 /// </summary> private static void BatchDelete() { Stopwatch watch = new Stopwatch(); Console.WriteLine("开始删除计时"); watch.Start(); using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>=1).Delete(); } watch.Stop(); Console.WriteLine("结束删除计时,工用时:{0}ms", watch.ElapsedMilliseconds); using (var context = new BatchDemoContext()) { var count = context.TestEntities.Count(); Console.WriteLine("数据库总共数据:{0}条", count); } Console.WriteLine("-----------------华丽的分割线 删除-------------------------"); } 

使用代码

using (var context = new BatchDemoContext()) { EFBatchOperation.For(context, context.TestEntities) .Where(c=>c.Id>=1).Delete(); } 

进行批量删除,当咱们删除以后,数据库数据应该为空,即条目为0,为了验证是否删除,咱们只需获取条目便可,运行结果以下

 
图片.png

实践证实,批量删除是成功的


总结

efUtilities地址:https://github.com/MikaelEliasson/EntityFramework.Utilities ,也能够从这里看到文档

相比 zzz projects ,其提供的功能还算是很全的,批量插入,批量更新,查询更新和批量删除,可是, ef utilities 是 免费开源的,免费开源的,免费开源的,重要的事情说五遍,开源的代码,咱们能够学习甚至是改造,打造符合本身的代码!

相比 ef extend , ef utilities 提供的功能全面,基本上是 extend有的,utilities 有,extend 没有的,utilities 也有,(只针对批量操做,查询方面,仍是extend 强大)

什么状况下,会用到批量操做?
我遇到的有:导入数据、录入多条数据、批量计算而后保存每一条数据等等………………

qq:961823316
源代码:https://git.oschina.net/zhaord/EfBatchDemo.git
转载注明:http://www.jianshu.com/p/dff3c684a0e4

相关文章
相关标签/搜索