一. 用法
1. 說明
關於該程序集詳細的介紹、背景、原理,直接去看老楊自己的文章。(支持:SQLServer、MySQL、Oracle、Postgresql、Sqlite,EFCore必須5.0以上)
文章參考:https://www.bilibili.com/read/cv8545714 https://mp.weixin.qq.com/s/t0wd5B_N_IWhN61xw0CxXw
(關於用法,要參考GitHub中的最新寫法!!!!!)
GitHub:https://github.com/yangzhongke/Zack.EFCore.Batch
總結:
該程序集實現的批量更新、批量刪除功能可以通過生成一條Update、Delete語句來實現,而不需要EFCore原始的寫法先查詢后操作了。
2. 基於SQLServer
首先要有4個基本的程序集,然后通過nuget安裝程序集【Zack.EFCore.Batch.MSSQL 1.4.9】,然后在DBContext上下文中OnConfiguring添加代碼 optionsBuilder.UseBatchEF_MSSQL();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build => { build.AddDebug(); })); optionsBuilder.UseBatchEF_MSSQL();// MSSQL Server 用戶用這個 }
(1). 批量刪除: DeleteRange 和 DeleteRangeAsync
代碼分享:
{ var count1 = dbContext.DeleteRange<T_UserInfor>(u => u.id == "1"); }
(2). 批量修改: BatchUpdate.Where().Execute() 和 .ExecuteAsync()
代碼分享:
{ var count2 = dbContext.BatchUpdate<T_UserInfor>() .Where(u => u.userSex.Contains("男")) .Set(u => u.userAge, u => u.userAge + 100) .Set(u => u.userName, u => "ypf001") .Execute(); }
PS:條件中支持關系對象(外鍵的模式)進行拼接
{ var count2 = dbContext.BatchUpdate<T_UserInfor>() .Where(u => u.UserRole.id="111") .Set(u => u.userAge, u => u.userAge + 100) .Set(u => u.userName, u => "ypf001") .Execute(); }
(3). 支持Take()、Skip()來限制刪除和更新數據的范圍
批量刪除和批量更新都支持通過Take()、Skip()來實現部分刪除和部分更新,例子代碼如下:
【1.4.9】中不好用
{ //存在bug skip和take均不生效,且count返回符合where條件的所有條數 //int count = dbContext.Set<T_UserInfor>().Where(u => u.id != "1").Skip(2).Take(3).DeleteRange<T_UserInfor>(dbContext); //存在bug skip和take均不生效,且count返回符合where條件的所有條數 //var count2 = dbContext.BatchUpdate<T_UserInfor>() // .Set(u => u.userAge, u => 100) // .Set(u => u.userName, u => "123") // .Where(u => u.userSex.Contains("男")) // .Skip(2) // .Take(3) // .Execute(); }
(4). 批量插入
BulkInsert()底層使用各個數據庫的BulkCopy機制實現數據插入,因此插入效率非常高。目前有如下兩個缺點:不支持關聯數據的自動插入,對於關聯的對象,請同樣調用BulkInsert()進行插入;由於PostgreSQL的.NET Core Provider還沒有支持BulkCopy,所以目前Zack.EFCore.Batch暫不支持PostgreSQL。
List<T_UserInfor> uList = new List<T_UserInfor>(); for (int i = 0; i < 1000; i++) { T_UserInfor user = new T_UserInfor(); user.id = Guid.NewGuid().ToString("N"); user.userName = "ypf" + i; user.userSex = "男" + i; user.userAge = i; user.addTime = DateTime.Now; uList.Add(user); } dbContext.BulkInsert(uList);
(5). 整合事務:可以集成到事務中
代碼分享:

{ using (var transaction = dbContext.Database.BeginTransaction()) { try { //業務1 T_UserInfor userInfor = new T_UserInfor() { id = Guid.NewGuid().ToString("N"), userName = "ypf", userSex = "男", userAge = 100, addTime = DateTime.Now }; dbContext.Add(userInfor); dbContext.SaveChanges(); //批量修改 var count2 = dbContext.BatchUpdate<T_UserInfor>() .Where(u => u.userSex.Contains("男")) .Set(u => u.userAge, u => u.userAge + 100) .Set(u => u.userName, u => "ypf1234") .Execute(); //模擬錯誤 T_UserInfor userInfor2 = new T_UserInfor() { id = Guid.NewGuid().ToString("N") + "fffff", //模擬錯誤 userName = "ypf1", userSex = "男1111", userAge = 111, addTime = DateTime.Now }; dbContext.Add(userInfor2); dbContext.SaveChanges(); //統一提交 transaction.Commit(); } catch (Exception ex) { //using包裹不需要手寫rollback Console.WriteLine(ex.Message); } } }
3. 基於MySQL
首先要有4個基本的程序集,然后通過nuget安裝程序集【Zack.EFCore.Batch.MySQL.Pomelo 1.3.0】,然后在DBContext上下文中OnConfiguring添加代碼 optionsBuilder.UseBatchEF_MySQLPomelo();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build => { build.AddDebug(); })); optionsBuilder.UseBatchEF_MySQLPomelo();//as for MySQL }
(1). 批量刪除: DeleteRange 和 DeleteRangeAsync
(2). 批量修改: BatchUpdate.Where.Set().Execute() 和 .ExecuteAsync()
(3). 整合事務:可以集成到事務中
代碼寫法同SQLServer
二. 橫向比較性能
1. 說明
關於批量修改和批量刪除,常用的程序集還有兩個,【EFCore.BulkExtensions】【Z.EntityFramework.Plus.EFCore】,但是前者不支持MySQL。
2. 測試結果
3. 代碼分享

{ using (EFDB01Context db = new EFDB01Context()) { Stopwatch watch = new Stopwatch(); watch.Start(); #region 批量刪除-EFCore.BulkExtensions //{ // int count1 = db.T_UserInfor.Where(u => u.userName == "1").BatchDelete(); //} #endregion #region 批量刪除-Z.EntityFramework.Plus.EFCore //{ // int count1 = db.T_UserInfor.Where(u => u.userName == "1").Delete(); //} #endregion #region 批量刪除-Zack.EFCore.Batch //{ // int count1 = db.DeleteRange<T_UserInfor>(u => u.userName == "1"); //} #endregion #region 批量修改-EFCore.BulkExtensions(全賦新值) //{ // int count1 = db.T_UserInfor.Where(u => u.userName == "1").BatchUpdate(new T_UserInfor() { userSex = "女12354", userAge = 100, addTime = DateTime.Now }); //} #endregion #region 批量修改-Z.EntityFramework.Plus.EFCore(全賦新值) //{ // int count1 = db.T_UserInfor.Where(u => u.id != "1").Update(x => new T_UserInfor() { userSex = "女12354", userAge = 100, addTime = DateTime.Now }); //} #endregion #region 批量修改-Zack.EFCore.Batch(全賦新值) //{ // int count1 = db.BatchUpdate<T_UserInfor>() // .Set(b => b.userSex, b => "女12354") // .Set(b => b.userAge, b => 100) // .Set(b => b.addTime, b => DateTime.Now) // .Execute(); //} #endregion #region 批量修改-Zack.EFCore.Batch(原值基礎上修改-上面時間基本一致) //{ // int count1 = db.BatchUpdate<T_UserInfor>() // .Set(b => b.userSex, b => b.userSex + "123") // .Set(b => b.userAge, b => b.userAge + 100) // .Set(b => b.addTime, b => DateTime.Now) // .Execute(); //} #endregion watch.Stop(); Console.WriteLine($"用時:{watch.ElapsedMilliseconds}"); //修改完刪掉 //db.Truncate<T_UserInfor>(); } }
三. 原理代碼剖析
詳見:https://www.bilibili.com/read/cv8545714
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。