第二十一节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2021-02-24 14:09  Yaopengfei  阅读(2672)  评论(9编辑  收藏  举报