第二十一節:EFCore5.0基於【Zack.EFCore.Batch 】實現批量插入/更新/刪除、橫向比較性能、剖析實現原理


一. 用法

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);
                        }
                    }
                }
View Code

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

                }
            }
View Code

 

三. 原理代碼剖析

 詳見:https://www.bilibili.com/read/cv8545714

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM