abpvnext及EFcore使用EFCore.BulkExtensions 批量增加、删除、修改,百万级数据测试通过
EFCore.BulkExtensions 简介
EntityFrameworkCore扩展:批量操作(插入,更新,删除,读取,更新,同步)和批处理(删除,更新)。
库是轻量级的,并且非常高效,具有所有最常用的CRUD操作。
在Microsoft推荐的EFcore扩展 Top 20。
当前版本使用的是EF Core 3.1,目前支持Microsoft SQL Server(2008+)和SQLite。
它针对NetStandard 2.0,因此可以用于针对NetCore(2.0+)或NetFramework(4.6.1+)的项目。
3.1.0和3.0.0之间的版本使用的是EF Core 3.0,并且以NetStandard 2.1为目标,因此只能在NetCore(3.0+)上使用。
3.0之前的版本(最后2.6.4)针对NetStandard 2.0,可以与NetCore(2.2)或NetFramework(4.6.1+)一起使用。
EFCore / v.Nuget:EFCore2.1 / v2.4.1 EFCore2.0 / v2.0.8,对于EF Core 1.x,请使用1.1.0(以NetStandard 1.4为目标)
请使用SqlBulkCopy进行插入,更新/删除合并具有原始Sql的 MERGE BulkInsert 。
对于SQLite,没有BulkCopy,而是库将普通SQL与UPSERT结合使用。
批量测试不能具有UseInMemoryDb,因为InMemoryProvider不支持特定于关系的方法。
在新版nuget上,用软件包管理器控制台命令安装:
Install-Package EFCore.BulkExtensions
用法
用法非常简单明了。
批量扩展是在DbContext类上进行的,可以像这样使用(支持常规和异步方法):
context.BulkInsert(entitiesList); context.BulkInsertAsync(entitiesList); context.BulkUpdate(entitiesList); context.BulkUpdateAsync(entitiesList); context.BulkDelete(entitiesList); context.BulkDeleteAsync(entitiesList); context.BulkInsertOrUpdate(entitiesList); context.BulkInsertOrUpdateAsync(entitiesList); //Upsert context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); //Sync context.BulkRead(entitiesList); context.BulkReadAsync(entitiesList); context.Truncate(); context.TruncateAsync();
批处理扩展是在IQueryable DbSet上进行的,可以在以下代码段中使用。
它们以纯sql的形式完成,并且不检查某些对象是否已预先装入内存并被跟踪。(updateColumns是可选参数,其中的PropertyNames在我们需要更新为其默认值时明确添加)
// Delete context.Items.Where(a => a.ItemId > 500).BatchDelete(); context.Items.Where(a => a.ItemId > 500).BatchDeleteAsync(); // Update (using Expression arg.) supports Increment/Decrement context.Items.Where(a => a.ItemId <= 500).BatchUpdate(a => new Item { Quantity = a.Quantity + 100 }); // can be as value '+100' or as variable '+incrementStep' (int incrementStep = 100;) // Update (via simple object) context.Items.Where(a => a.ItemId <= 500).BatchUpdate(new Item { Description = "Updated" }); context.Items.Where(a => a.ItemId <= 500).BatchUpdateAsync(new Item { Description = "Updated" }); // Update (via simple object) - requires additional Argument for setting to Property default value var updateColumns = new List { nameof(Item.Quantity) }; // Update 'Quantity' to default value('0'-zero) var q = context.Items.Where(a => a.ItemId <= 500); int affected = q.BatchUpdate(new Item { Description = "Updated" }, updateColumns);//result assigned to variable
批量操作
直接使用这些操作时,每个操作都是独立的事务,并且会自动提交。
如果我们需要在单个过程中执行多个操作,则应使用显式事务,例如:
using (var transaction = context.Database.BeginTransaction()) { context.BulkInsert(entitiesList); context.BulkInsert(subEntitiesList); transaction.Commit(); }
在abpvnext实际项目中,有EFCore中应用,效率非常高,用前端vue导入调用后端此方式的接口进行导入100W+级数据量(Excel格式),在一分钟内(有相关检验操作,并非简单插入操作)导入成功!
private readonly IRepository<BranchRole> _branchRoleRepository; public UserAppService( IRepository<BranchRole> branchRoleRepository) { _branchRoleRepository = branchRoleRepository; } //方法中核心代码如下 await _branchRoleRepository.GetDbContext().BulkInsertAsync<BranchRole>(entityList);
1. 说明
通过Nuget安装程序集【EFCore.BulkExtensions】,该程序集目前版本【3.1.0】,支持CRUD操作,支持的数据库仅有:SQLServer和SQLite,它是免费开源的。
GitHub地址:https://github.com/borisdj/EFCore.BulkExtensions
2. 用法说明
(1).Bulk相关(一条操作一个事务,均是传入实体)
A.增加:BulkInsert
B.修改:BulkUpdate,需要传入完整实体,不传的字段就会被更新为空
C.增加或修改:BulkInsertOrUpdate (主键存在执行update,不存在执行insert)
D.删除:BulkDelete 和 Truncate(删除整张表)
//1. 增加 List<T_UserInfor> ulist1 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf", userSex = "男", userAge = 111, addTime = DateTime.Now }; ulist1.Add(userInfor); } dbContext.BulkInsert(ulist1); //2. 修改 List<T_UserInfor> ulist2 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { //此处不写的字段就会被更新成null了 T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf1", }; ulist2.Add(userInfor); } dbContext.BulkUpdate(ulist2); //3. 删除 List<T_UserInfor> ulist3 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { //此处不写的字段就会被更新成null了 T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), }; ulist3.Add(userInfor); } dbContext.BulkDelete(ulist3);
(2).Batch相关
A.条件删除:BatchDelete
B.条件更新:BatchUpdate (可以基于原有数据)
PS:以上方法均支持Async异步方法。
//4.条件删除 int count1 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchDelete(); //5. 条件更新 //5.1 基于原有数据改 int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { userAge = a.userAge + 1 }); //5.2 直接改成新数据 int count3 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(new T_UserInfor() { userSex = "女" });
(3).事务
和正常用法一样, using(var transaction = dbContext.Database.BeginTransaction())包裹,有using的情况下, catch中不用写rollback,走完using如果报错会自动回滚。
这种写法仅支持SQLServer,Sqlite中写法不一样,注意以下方式在纯的EFCore中应用是可以的,但是不适用于abpvnext,原因接下来会做说明。
using (var transaction = dbContext.Database.BeginTransaction()) { try { List<T_UserInfor> ulist1 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf", userSex = "男", userAge = 111, addTime = DateTime.Now }; ulist1.Add(userInfor); } dbContext.BulkInsert(ulist1); int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { id = a.id + "fsdfsdfsdfsfdsadfsadfsdfsfsafsfsdfsdfsdfsdf" }); //统一提交 transaction.Commit(); } catch (Exception ex) { //using包裹不需要手写rollback Console.WriteLine(ex.Message); } }
说明:在abpvnext中显示加入以上数据库事务(例如:var transaction = dbContext.Database.BeginTransaction()),会报错,大致的意思是事务冲突,主要是abpvnext中的工作单元导致,即使禁止UOW也报错,原因未知待研究,目前在项目中的应用就是不显示加入context.Database.BeginTransaction事务。
using (var transaction = context.Database.BeginTransaction()) { context.BulkInsert(entities1List); context.BulkInsert(entities2List); transaction.Commit(); }
(4).相关配置
可以配置的参数有:PreserveInsertOrder, SetOutputIdentity, BatchSize, NotifyAfter, BulkCopyTimeout, EnableStreaming, UseTempDB, TrackingEntities,UseOnlyDataTable, WithHoldlock, CalculateStats, StatsInfo, PropertiesToInclude, PropertiesToExclude, UpdateByProperties, SqlBulkCopyOptions . 根据自己的情况选择使用吧
配置文件:实例化BulkConfig,在实际应用中遇到过插入100W+级数据,设置了配置文件比如BatchSize = 10000,发现有时还会出现数据库连接超时的问题(相关web.config也做了超时配置),按照其原理是每插入1W连接一次数据,应该不会超时才对,但是就是报超时,最后的解决办法就是去掉了相关配置,好使了。如果以上文章对您有一点作用,请帮点个推荐,谢谢。
var bulkConfig = new BulkConfig { SetOutputIdentity = true, BatchSize = 4000 };
context.BulkInsert(entList, bulkConfig);
context.BulkInsertOrUpdate(entList, new BulkConfig { SetOutputIdentity = true });
context.BulkInsertOrUpdate(entList, b => b.SetOutputIdentity = true); // example of BulkConfig set with Action arg.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)