第十九節:EFCore5.0基於【Pomelo.EntityFrameworkCore.MySql】和【MySql.EntityFrameworkCore】連接MySQL各場景測試


一. 說明

1. 環境准備

(1).測試載體:.Net 5.0  控制台

(2).數據庫:MySQL 5.7 (事先改為大小寫敏感 lower_case_table_names=0)

(3).EFCore相關程序集:(這里統一用5.0.6 版本

 Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.Design、Microsoft.EntityFrameworkCore.Tools

(4).MySQL相關程序集:

 A. MySQL官方出品:MySql.EntityFrameworkCore (5.0.3.1)                           

(舊版的MySql.Data.EntityFrameworkCore 8.0.22 官方不再維護)

 B. 社區出品:Pomelo.EntityFrameworkCore.MySql (版本:5.0.0)                     【github地址:https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql】

2. 配置日志

 為了便於調優,配置日志,把linq轉換成SQL輸出來.

 安裝程序集:【Microsoft.Extensions.Logging.Console】

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  {
            optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build =>
            {
                build.AddConsole();  // 用於控制台程序的輸出
            }));
  }

3. DB准備

 這里為了配合框架,使用的CoreFrameDB數據庫,詳見數據庫說明書,並且新增一張T_Test,用於演示MySQL各種數據類型的映射.

 

 

 

二. 映射剖析

1. 映射指令測試

 A.首次映射:詳見指令文檔

 B.修改后映射:-Force,會把上下午和全部實體的變化都修改一下

 C.局部映射: -tables,根據情況選擇使用,他會把上下文中已經多余的關聯實體關系刪掉,僅保留 -tables后面表, 多余的實體並不修改, 謹慎使用。

 D. 不保留復數: -NoPluralize

指令分享:

(1). Pomelo.EntityFrameworkCore.MySql

--全局新增
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" Pomelo.EntityFrameworkCore.MySql -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -DataAnnotations -NoPluralize

--全局修改
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" Pomelo.EntityFrameworkCore.MySql -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -DataAnnotations  -Force -NoPluralize

-- 部分修改,沒有上面bug
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" Pomelo.EntityFrameworkCore.MySql  -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -Tables T_Test -DataAnnotations  -Force -NoPluralize

(2). MySql.EntityFrameworkCore

--全局新增
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" MySql.EntityFrameworkCore -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -DataAnnotations -NoPluralize

--全局修改
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" MySql.EntityFrameworkCore -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -DataAnnotations  -Force -NoPluralize

-- 部分修改,沒有上面bug
Scaffold-DbContext "Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456;" MySql.EntityFrameworkCore -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -Tables T_Test -DataAnnotations  -Force -NoPluralize

映射上下文和部分實體分享:

(1). Pomelo.EntityFrameworkCore.MySql

       protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build =>
            {
                build.AddConsole();  // 用於控制台程序的輸出
            }));

            optionsBuilder.UseMySql("server=xxxxx;database=CoreFrameDB;user id=root;password=123456", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"));
        }

(2). MySql.EntityFrameworkCore

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build =>
            {
                build.AddConsole();  // 用於控制台程序的輸出
            }));

            optionsBuilder.UseMySQL("Server=xxxx;Database=CoreFrameDB;User ID=root;Password=123456;");
        }

注:mysql中的 timestamp類型對應C#中的是DateTime類型,但是EFCore使用MySql.EntityFrameworkCore】無法反向生成,使用【Pomelo.EntityFrameworkCore.MySql】可以反向生成。

  針對無法映射生成的類型,會自動跳過,這個時候如果使用的是MySql.EntityFrameworkCore】映射,需要自己根據類型手動加上即可。

2.數據類型對應測試

3.補充EFCore數據注解屬性

 參考:https://docs.microsoft.com/zh-cn/ef/core/modeling/

      https://www.cnblogs.com/yaopengfei/p/7813120.html

 部分特性如下: [NotMapped]、 [Required]、 [Column("blog_id")]、 [Key]、 [DatabaseGenerated(DatabaseGeneratedOption.Identity)]、 [ConcurrencyCheck]、 [Timestamp]。

PS:關於數據注解后續再EFCore系列中單獨寫一個章節進行測試。

 

三. 實戰測試1

前言:

 以下測試都基於【Pomelo.EntityFrameworkCore.MySql】來進行,連接字符串如下:

optionsBuilder.UseMySql("server=xxxx;database=CoreFrameDB;user id=root;password=xxxx", Microsoft.EntityFrameworkCore.ServerVersion.Parse("5.7.28-mysql"));

1. 基礎CRUD(+封裝)

  經測試,均可正常使用。

封裝方法:

 /// <summary>
    /// 泛型方法,直接注入EF上下文
    /// </summary>
    public class BaseService { 
        public DbContext db;

        /// <summary>
        /// 在使用的時候,自動注入db上下文
        /// </summary>
        /// <param name="db"></param>
        public BaseService(DbContext db)
        {
            this.db = db;

            //關閉全局追蹤的代碼
            //db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        }

        /****************************************下面進行方法的封裝(同步)***********************************************/
        //1. 直接提交數據庫

        #region 01-數據源
        public IQueryable<T> Entities<T>() where T : class
        {
            return db.Set<T>();
        }

        public IQueryable<T> EntitiesNoTrack<T>() where T : class
        {
            return db.Set<T>().AsNoTracking();
        }

        #endregion

        #region 02-新增
        public int Add<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Added;
            return db.SaveChanges();

        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        /// <returns></returns>
        public int Del<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
            return db.SaveChanges();
        }
        #endregion

        #region 04-根據條件刪除(支持批量刪除)
        /// <summary>
        /// 根據條件刪除(支持批量刪除)
        /// </summary>
        /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param>
        /// <returns></returns>
        public int DelBy<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = db.Set<T>().Where(delWhere).ToList();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
            return db.SaveChanges();
        }
        #endregion

        #region 05-單實體修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        /// <returns></returns>
        public int Modify<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
            return db.SaveChanges();
        }
        #endregion

        #region 06-批量修改(非lambda)
        /// <summary>
        /// 批量修改(非lambda)
        /// </summary>
        /// <param name="model">要修改實體中 修改后的屬性 </param>
        /// <param name="whereLambda">查詢實體的條件</param>
        /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
        /// <returns></returns>
        public int ModifyBy<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class
        {
            List<T> listModifes = db.Set<T>().Where(whereLambda).ToList();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>
            {
                if (proNames.Contains(p.Name))
                {
                    dicPros.Add(p.Name, p);
                }
            });
            foreach (string proName in proNames)
            {
                if (dicPros.ContainsKey(proName))
                {
                    PropertyInfo proInfo = dicPros[proName];
                    object newValue = proInfo.GetValue(model, null);
                    foreach (T m in listModifes)
                    {
                        proInfo.SetValue(m, newValue, null);
                    }
                }
            }
            return db.SaveChanges();
        }
        #endregion

        #region 07-根據條件查詢
        /// <summary>
        /// 根據條件查詢
        /// </summary>
        /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetListBy<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class
        {
            if (isTrack)
            {
                return db.Set<T>().Where(whereLambda).ToList();
            }
            else
            {
                return db.Set<T>().Where(whereLambda).AsNoTracking().ToList();
            }
           
        }
        #endregion

        #region 08-根據條件排序和查詢
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetListBy<T, Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            IQueryable<T> data = null;         
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda);
            }
            else
            {
                data = data.OrderByDescending(orderLambda);
            }
            return data.ToList();
        }
        #endregion

        #region 09-分頁查詢(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageList<T, Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {

            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            return data.ToList();
        }
        #endregion

        #region 10-分頁查詢(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageListByName<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            List<T> list = null;
            if (isTrack)
            {
                list = db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            else
            {
                list = db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            return list;
        }
        #endregion

        #region 11-分頁查詢輸出總行數(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageList<T, Tkey>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            int count = db.Set<T>().Where(whereLambda).Count();
            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data=data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            rowCount = count;
            return data.ToList();
        }
        #endregion

        #region 12-分頁查詢輸出總行數(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageListByName<T>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            int count = 0;
            count = db.Set<T>().Where(whereLambda).Count();
            List<T> list = null;
            if (isTrack)
            {
                list= db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            else
            {
                list = db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                   .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            }
            rowCount = count;
            return list;
        }
        #endregion


        //2. SaveChange剝離出來,處理事務

        #region 01-批量處理SaveChange()
        /// <summary>
        /// 事務批量處理
        /// </summary>
        /// <returns></returns>
        public int SaveChange()
        {
            return db.SaveChanges();
        }
        #endregion

        #region 02-新增
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="model">需要新增的實體</param>
        public void AddNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Added;
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        public void DelNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
        }
        #endregion

        #region 04-根據條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public void DelByNo<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = db.Set<T>().Where(delWhere).ToList();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
        }
        #endregion

        #region 05-修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        public void ModifyNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
        }
        #endregion


        //3. EF調用sql語句

        #region 01-執行增加,刪除,修改操作(或調用相關存儲過程)
        /// <summary>
        /// 執行增加,刪除,修改操作(或調用存儲過程)
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public int ExecuteSql(string sql, params SqlParameter[] pars)
        {
            return db.Database.ExecuteSqlRaw(sql, pars);
        }

        #endregion

        #region 02-執行查詢操作(調用查詢類的存儲過程)
        /// <summary>
        /// 執行查詢操作
        /// 注:查詢必須返回實體的所有屬性字段;結果集中列名必須與屬性映射的項目匹配;查詢中不能包含關聯數據
        /// 除Select以外其他的SQL語句無法執行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> ExecuteQuery<T>(string sql, bool isTrack = true, params SqlParameter[] pars) where T : class
        {
            if (isTrack)
            {
                //表示跟蹤狀態(默認是跟蹤的)
                return db.Set<T>().FromSqlRaw(sql, pars).ToList();
            }
            else
            {
                //表示不跟蹤狀態
                return db.Set<T>().FromSqlRaw(sql, pars).AsNoTracking().ToList();
            }
        }
        #endregion

        #region 03-執行查詢操作(與Linq相結合)
        /// <summary>
        /// 執行查詢操作
        /// 注:查詢必須返回實體的所有屬性字段;結果集中列名必須與屬性映射的項目匹配;查詢中不能包含關聯數據
        /// 除Select以外其他的SQL語句無法執行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        ///  <param name="whereLambda">查詢條件</param>
        /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> ExecuteQueryWhere<T>(string sql, Expression<Func<T, bool>> whereLambda, bool isTrack = true, params SqlParameter[] pars) where T : class
        {
            if (isTrack)
            {
                //表示跟蹤狀態(默認是跟蹤的)
                return db.Set<T>().FromSqlRaw(sql, pars).Where(whereLambda).ToList();
            }
            else
            {
                //表示不跟蹤狀態
                return db.Set<T>().FromSqlRaw(sql, pars).Where(whereLambda).AsNoTracking().ToList();
            }
        }
        #endregion



        /****************************************下面進行方法的封裝(異步)***********************************************/

        #region 01-新增
        public async Task<int> AddAsync<T>(T model) where T : class
        {
            await db.AddAsync(model);
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 02-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        /// <returns></returns>
        public async Task<int> DelAsync<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 03-根據條件刪除(支持批量刪除)
        /// <summary>
        /// 根據條件刪除(支持批量刪除)
        /// </summary>
        /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param>
        /// <returns></returns>
        public async Task<int> DelByAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = await db.Set<T>().Where(delWhere).ToListAsync();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 04-單實體修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        /// <returns></returns>
        public async Task<int> ModifyAsync<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 05-批量修改(非lambda)
        /// <summary>
        /// 批量修改(非lambda)
        /// </summary>
        /// <param name="model">要修改實體中 修改后的屬性 </param>
        /// <param name="whereLambda">查詢實體的條件</param>
        /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
        /// <returns></returns>
        public async Task<int> ModifyByAsync<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class
        {
            List<T> listModifes = await db.Set<T>().Where(whereLambda).ToListAsync();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>
            {
                if (proNames.Contains(p.Name))
                {
                    dicPros.Add(p.Name, p);
                }
            });
            foreach (string proName in proNames)
            {
                if (dicPros.ContainsKey(proName))
                {
                    PropertyInfo proInfo = dicPros[proName];
                    object newValue = proInfo.GetValue(model, null);
                    foreach (T m in listModifes)
                    {
                        proInfo.SetValue(m, newValue, null);
                    }
                }
            }
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 06-根據條件查詢
        /// <summary>
        /// 根據條件查詢
        /// </summary>
        /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetListByAsync<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class
        {
            if (isTrack)
            {
                return await db.Set<T>().Where(whereLambda).ToListAsync();
            }
            else
            {
                return await db.Set<T>().Where(whereLambda).AsNoTracking().ToListAsync();
            }

        }
        #endregion

        #region 07-根據條件排序和查詢
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetListByAsync<T, Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda);
            }
            else
            {
                data = data.OrderByDescending(orderLambda);
            }
            return await data.ToListAsync();
        }
        #endregion

        #region 08-分頁查詢(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>>  GetPageListAsync<T, Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {

            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            return await data.ToListAsync();
        }
        #endregion

        #region 09-分頁查詢(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetPageListByNameAsync<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            List<T> list = null;
            if (isTrack)
            {
                list = await db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            }
            else
            {
                list = await db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            }
            return list;
        }
        #endregion

 

        //2. SaveChange剝離出來,處理事務

        #region 01-批量處理SaveChange()
        /// <summary>
        /// 事務批量處理
        /// </summary>
        /// <returns></returns>
        public async Task<int> SaveChangeAsync()
        {
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 02-新增
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="model">需要新增的實體</param>
        public async Task<EntityEntry<T>> AddNoAsync<T>(T model) where T : class
        {
           return await db.AddAsync(model);
        }
        #endregion
        
        #region 03-根據條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public async Task DelByNoAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels =await db.Set<T>().Where(delWhere).ToListAsync();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
        }
        #endregion


        //3. EF調用sql語句

        #region 01-執行增加,刪除,修改操作(或調用存儲過程)
        /// <summary>
        /// 執行增加,刪除,修改操作(或調用存儲過程)
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public async Task<int> ExecuteSqlAsync(string sql, params SqlParameter[] pars)
        {
            return await db.Database.ExecuteSqlRawAsync(sql, pars);
        }
        #endregion


        /****************************************下面是基於【EFCore.BulkExtensions】大數據的處理 (同步)***********************************************/

        #region 01-增加
        /// <summary>
        /// 增加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkInsert<T>(List<T> list) where T : class
        {
            db.BulkInsert<T>(list);
        }
        #endregion

        #region 02-修改
        /// <summary>
        /// 修改
        /// PS:傳入的實體如果不賦值,則更新為null,即傳入的實體每個字段都要有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkUpdate<T>(List<T> list) where T : class
        {
            db.BulkUpdate<T>(list);
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// PS:傳入的list中的實體僅需要主鍵有值,它是根據主鍵刪除的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkDelete<T>(List<T> list) where T : class
        {
            db.BulkDelete<T>(list);
        }
        #endregion

        #region 04-條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public int BatchDelete<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            return db.Set<T>().Where(delWhere).BatchDelete();
        }
        #endregion

        #region 05-條件更新1
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public int BatchUpdate<T>(Expression<Func<T, bool>> delWhere, T model) where T : class, new()
        {
            return db.Set<T>().Where(delWhere).BatchUpdate(model);
        }
        #endregion

        #region 06-條件更新2
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public int BatchUpdate2<T>(Expression<Func<T, bool>> delWhere, Expression<Func<T, T>> modelWhere) where T : class, new()
        {
            return db.Set<T>().Where(delWhere).BatchUpdate(modelWhere);
        }
        #endregion


        /****************************************下面是基於【EFCore.BulkExtensions】大數據的處理 (異步)***********************************************/

        #region 01-增加
        /// <summary>
        /// 增加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkInsertAsync<T>(List<T> list) where T : class
        {
            await db.BulkInsertAsync<T>(list);
        }
        #endregion

        #region 02-修改
        /// <summary>
        /// 修改
        /// PS:傳入的實體如果不賦值,則更新為null,即傳入的實體每個字段都要有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkUpdateAsync<T>(List<T> list) where T : class
        {
            await db.BulkUpdateAsync<T>(list);
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// PS:傳入的list中的實體僅需要主鍵有值,它是根據主鍵刪除的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkDeleteAsync<T>(List<T> list) where T : class
        {
            await db.BulkDeleteAsync<T>(list);
        }
        #endregion

        #region 04-條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public async Task<int> BatchDeleteAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            return await db.Set<T>().Where(delWhere).BatchDeleteAsync();
        }
        #endregion

        #region 05-條件更新1
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public async  Task<int> BatchUpdateAsync<T>(Expression<Func<T, bool>> delWhere, T model) where T : class, new()
        {
            return await db.Set<T>().Where(delWhere).BatchUpdateAsync(model);
        }
        #endregion

        #region 06-條件更新2
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public async Task<int> BatchUpdate2Async<T>(Expression<Func<T, bool>> delWhere, Expression<Func<T, T>> modelWhere) where T : class, new()
        {
            return await db.Set<T>().Where(delWhere).BatchUpdateAsync(modelWhere);
        }
        #endregion

    }
 /// <summary>
    /// 排序的擴展
    /// </summary>
    public static class SortExtension
    {

        #region 01-根據string名稱排序擴展(單字段)
        /// <summary>
        /// 根據string名稱排序擴展(單字段)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">排序數據源</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">排序方式 asc或desc</param>
        /// <returns></returns>
        public static IQueryable<T> DataSorting<T>(this IQueryable<T> source, string sortName, string sortDirection)
        {
            string sortingDir = string.Empty;
            if (sortDirection.ToUpper().Trim() == "ASC")
            {
                sortingDir = "OrderBy";
            }
            else if (sortDirection.ToUpper().Trim() == "DESC")
            {
                sortingDir = "OrderByDescending";
            }
            ParameterExpression param = Expression.Parameter(typeof(T), sortName);
            PropertyInfo pi = typeof(T).GetProperty(sortName);
            Type[] types = new Type[2];
            types[0] = typeof(T);
            types[1] = pi.PropertyType;
            Expression expr = Expression.Call(typeof(Queryable), sortingDir, types, source.Expression, Expression.Lambda(Expression.Property(param, sortName), param));
            IQueryable<T> query = source.AsQueryable().Provider.CreateQuery<T>(expr);
            return query;
        }
        #endregion

        #region 02-根據多個string名稱排序擴展(多字段)
        /// <summary>
        ///  根據多個string名稱排序擴展(多字段)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">數據源</param>
        /// <param name="orderParams">排序類</param>
        /// <returns></returns>
        public static IQueryable<T> DataManySorting<T>(this IQueryable<T> data, params FiledOrderParam[] orderParams) where T : class
        {
            var parameter = Expression.Parameter(typeof(T), "p");
            if (orderParams != null && orderParams.Length > 0)
            {
                for (int i = 0; i < orderParams.Length; i++)
                {
                    var property = typeof(T).GetProperty(orderParams[i].PropertyName);
                    if (property != null)
                    {
                        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                        var orderByExpr = Expression.Lambda(propertyAccess, parameter);
                        string methodName = i > 0 ?
                            orderParams[i].IsDesc ? "ThenByDescending" : "ThenBy"
                            : orderParams[i].IsDesc ? "OrderByDescending" : "OrderBy";
                        var resultExp = Expression.Call(
                            typeof(Queryable), methodName,
                            new Type[] { typeof(T), property.PropertyType },
                            data.Expression, Expression.Quote(orderByExpr)
                            );
                        data = data.Provider.CreateQuery<T>(resultExp);
                    }
                }
            }
            return data;
        }

        #endregion
    }


    /// <summary>
    /// 排序類
    /// </summary>
    public class FiledOrderParam
    {
        //是否降序
        public bool IsDesc { get; set; }
        //排序名稱
        public string PropertyName { get; set; }
    }
View Code

測試代碼: 

               #region 01-增加
                {
                    //T_Test tTest = new T_Test()
                    //{
                    //    age1 = 1,
                    //    age2 = 2,
                    //    age3 = 3,
                    //    age4 = 4,
                    //    age5 = 5,
                    //    money1 = (float)10.12,
                    //    money2 = 20.34,
                    //    money3 = (decimal)12.13,
                    //    addTime1 = 2020,
                    //    addTime2 = DateTime.Now,
                    //    addTime3 = TimeSpan.FromMinutes(10),
                    //    addTime4 = DateTime.Now,
                    //    addTime5 = DateTime.Now,
                    //    name1 = "ypf1",
                    //    name2 = "ypf2",
                    //    name3 = "ypf3",
                    //    name4 = "ypf4",
                    //    name5 = "ypf5",
                    //    name6 = "ypf6",
                    //    isSex1 = 1
                    //};
                    //int count1 = baseService.Add(tTest);

                    //T_SysErrorLog sErrorLog = new T_SysErrorLog();
                    //sErrorLog.id = Guid.NewGuid().ToString("N");
                    //sErrorLog.userId = "001";
                    //sErrorLog.userAccount = "12345";
                    //sErrorLog.logLevel = "Error";
                    //sErrorLog.logMessage = "出錯了";
                    //sErrorLog.addTime = DateTime.Now;
                    //sErrorLog.delFlag = 0;
                    //int count2 = baseService.Add(sErrorLog);
                }
                #endregion

                #region 02-修改
                //{
                //    var data = baseService.Entities<T_SysErrorLog>().Where(u => u.id == "1").FirstOrDefault();
                //    data.userAccount = "123456";
                //    baseService.SaveChange();
                //    Console.WriteLine("修改成功");
                //}
                #endregion

                #region 03-刪除
                //{
                //    baseService.DelBy<T_SysErrorLog>(u => u.id != "1");
                //    Console.WriteLine("刪除成功");
                //}
                #endregion

                #region 04-根據條件查詢和排序
                //{
                //    var list = baseService.GetListBy<T_SysErrorLog, DateTime?>(u => u.id != "xxx", p => p.addTime, false);
                //    foreach (var item in list)
                //    {
                //        Console.WriteLine($"id={item.id},userId={item.userId},userAccount={item.userAccount},addTime={item.addTime}");
                //    }

                //} 
                #endregion

                #region 05-根據字段名稱升/降序分頁查詢
                //{
                //    int pageIndex = 1;
                //    int pageSize = 2;
                //    //1.分開寫法
                //    var list1 = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "fk").DataSorting("addTime", "desc").Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                //    //2. 封裝調用
                //    int count = 0;
                //    var list2 = baseService.GetPageListByName<T_SysErrorLog>(pageIndex, pageSize, out count, u => u.id != "fk", "addTime", "desc");

                //    //3.多字段排序
                //    FiledOrderParam[] param = {
                //        new FiledOrderParam(){IsDesc=false,PropertyName="addTime"},
                //        new FiledOrderParam(){IsDesc=true,PropertyName="id"}
                //    };
                //    var list3 = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "fk").DataManySorting(param).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                //}
                #endregion
View Code

2. 各種Linq測試

 詳細測試了各種linq對應MySQL的翻譯,代碼如下:

                #region 01-First/Last/Single/ElementAt
                //{
                //    //說明:First/FirstOrDefault 翻譯limit 1, Last/LastOrDefault不能翻譯  Single/SingleOrDefault翻譯成limit 2, ElementAt/ElementAtOrDefault不能翻譯

                //    db.T_SysErrorLog.First();
                //    db.T_SysErrorLog.FirstOrDefault();

                //    db.T_SysErrorLog.Last();
                //    db.T_SysErrorLog.LastOrDefault();

                //    var d1 = db.T_SysErrorLog.Single();
                //    var d2 = db.T_SysErrorLog.SingleOrDefault();

                //    var d3 = db.T_SysErrorLog.ElementAt(1);
                //    var d4 = db.T_SysErrorLog.ElementAtOrDefault(1);

                //}
                #endregion

                #region 02-Select相關
                //{
                //    //正常翻譯select
                //    var d1 = (from a in db.T_SysErrorLog
                //              select a.logLevel).ToList();
                //    //翻譯出來別名和計算
                //    var d2 = (from a in db.T_SysErrorLog
                //              select new
                //              {
                //                  a.logLevel,
                //                  a.addTime,
                //                  myMsg = a.logMessage,
                //                  myflag = a.delFlag / 2
                //              }).ToList();

                //    //這種嵌套無法翻譯,報錯
                //    var d3 = (from a in db.T_SysErrorLog
                //              select new
                //              {
                //                  a.logLevel,
                //                  a.addTime,
                //                  myMsg = from b in db.T_SysUser
                //                          select b.userAccount,
                //                  myflag = a.delFlag / 2
                //              }).ToList();

                //}
                #endregion

                #region 03-基本函數
                {
                    //1.以下基礎函數都可以翻譯成對應的mysql中的函數
                    //db.T_SysErrorLog.Count();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Sum();
                    //db.T_SysErrorLog.Sum(o => o.delFlag);
                    //db.T_SysErrorLog.Select(o => o.delFlag).Max();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Min();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Average();



                }
                #endregion

                #region 04-關聯查詢
                //{

                //    //隱式內連接 翻譯成 cross join (在mysql中,join、cross join、inner join含義相同)
                //    var data1 = (from a in db.T_SysUser
                //                 from b in db.T_SysLoginLog
                //                 where a.id == b.userId
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     b.loginCity,
                //                     b.loginIp,
                //                     b.loginTime
                //                 }).ToList();

                //    //顯式內連接 翻譯成inner join 
                //    var data2 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     b.loginCity,
                //                     b.loginIp,
                //                     b.loginTime
                //                 }).ToList();

                //    //外鏈接翻譯成 left join (linq中通過顛倒數據位置實現left 或 right join)
                //    var data3 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId into fk
                //                 from c in fk.DefaultIfEmpty()
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     c.loginCity,
                //                     c.loginIp,
                //                     c.loginTime
                //                 }).ToList();

                //    //統計右表的數量,報錯,翻譯不出來!!
                //    var data4 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId into fk
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     myCount = fk.Count()
                //                 }).ToList();


                //}
                #endregion

                #region 05-排序
                //{
                //    //升序和降序 正常翻譯 mysql的排序
                //    var d1 = db.T_SysErrorLog.OrderBy(u => u.addTime).ThenByDescending(u => u.delFlag).ToList();
                //    var d2 = (from a in db.T_SysErrorLog
                //              orderby a.addTime, a.delFlag descending
                //              select a).ToList();
                //}
                #endregion

                #region 06-分組
                //{

                //    //前提:必須加.AsEnumerable(),否則報錯
                //    //以下經測試均可以使用,但是翻譯的sql語句不顯示group
                //    var d1 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by a.delFlag into g
                //              select g).ToList();

                //    var d2 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by a.delFlag into g
                //              select new
                //              {
                //                  myKey = g.Key,
                //                  g
                //              }).ToList();

                //    var d3 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by new { myFlag = a.delFlag != 2 } into g
                //              select new
                //              {
                //                  myKey = g.Key,
                //                  g
                //              }).ToList();
                //}
                #endregion

                #region 07-分頁
                //{
                //    //翻譯成mysql的 limit+offset用法,注意不是單獨的limit用法
                //    var d1 = db.T_SysErrorLog.Skip(2).Take(10).ToList();

                //    //下面兩句都報錯,無法翻譯
                //    var d2 = db.T_SysErrorLog.SkipWhile(u => u.delFlag ==0).ToList();
                //    var d3 = db.T_SysErrorLog.TakeWhile(u => u.delFlag == 0).ToList();
                //}
                #endregion

                #region 08-Contains/EF.Functions.Like/Concat/Union/Intersect/Except
                //{
                //    //1. 這里成Contains翻譯到mysql中的in
                //    string[] myList = { "222", "333", "444" };
                //    string mystr = "222,333,444";
                //    var d1 = db.T_SysErrorLog.Where(u => myList.Contains(u.logLevel)).ToList();
                //    // 這里的contians翻譯成 LIKE '%222%'     (sqlserver翻譯成charindex)
                //    var d2 = db.T_SysErrorLog.Where(u => u.logLevel.Contains("222")).ToList();
                //    //這里的contians翻譯成 LOCATE
                //    //補充:locate(subStr,string) :函數返回subStr在string中出現的位置
                //    var d3 = db.T_SysErrorLog.Where(u => mystr.Contains(u.logLevel)).ToList();

                //    //2. 翻譯成 LIKE '%222%
                //    var d4 = db.T_SysErrorLog.Where(u => EF.Functions.Like(u.logLevel, "%222%")).ToList();

                //    //3. 翻譯成Union All 不去重
                //    var d5 = ((from a in db.T_SysUser select a.id)
                //           .Concat
                //           (from a in db.T_SysRole select a.id)).ToList();
                //    //翻譯成Union 去重
                //    var d6 = ((from a in db.T_SysUser select a.id)
                //        .Union
                //        (from a in db.T_SysRole select a.id)).ToList();
                //    //無法翻譯報錯
                //    var d7 = ((from a in db.T_SysUser select a.id)
                //        .Intersect
                //        (from a in db.T_SysRole select a.id)).ToList();

                //    //無法翻譯報錯
                //    var d8= ((from a in db.T_SysUser select a.id)
                //        .Except
                //        (from a in db.T_SysRole select a.id)).ToList();


                //}
                #endregion

                #region 09-DateTime/String部分方法
                //{
                //    //翻譯成 EXTRACT方法
                //    var d1 = (from a in db.T_SysErrorLog
                //             where a.addTime.Value.Year == 2019
                //             select a).ToList();
                //    //翻譯成 Like
                //    var d2 = (from a in db.T_SysErrorLog
                //              where a.logLevel.StartsWith("333")
                //              select a).ToList();

                //    //翻譯成 SUBSTRING
                //    var d3 = (from a in db.T_SysErrorLog
                //             select a.logLevel.Substring(0,5)).ToList();
                //}
                #endregion
View Code

3. 調用SQL語句

  經測試,可以正常使用。需要注意的是參數化查詢要用 MySqlParameter。

代碼分享:

 #region 01-查詢類(很雞肋,只能單表全部查詢,不能指定字段)
                //{
                //    //1.基本的原生SQL查詢
                //    var userList1 = db.Set<T_SysErrorLog>().FromSqlRaw("select * from T_SysErrorLog where id!='123'").ToList();

                //    //2.利用$內插語法進行傳遞
                //    var myId = "1";
                //    var userList2 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog where id= {myId}").ToList();

                //    //3.原生SQL與linq語法相結合
                //    var userList3 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog")
                //        .Where(u => u.id == "2")
                //        .ToList();
                //    var userList4 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog")
                //        .Where(u => u.id != "1111")
                //        .OrderBy(u => u.addTime)
                //        .ToList();

                //    //4.利用SqlParameter進行參數化查詢 MySql.Data.MySqlClient.MySqlParameter
                //    MySqlParameter[] paras ={
                //                             new MySqlParameter("@id","2fc343069e0a4a559b62b08d5999dbcd"),
                //                             new MySqlParameter("@userAccount","ypf"),
                //                        };
                //    var userList5 = db.Set<T_SysErrorLog>().FromSqlRaw("select * from T_SysErrorLog where id=@id and userAccount=@userAccount", paras).ToList();

                //} 
                #endregion

                #region 02-執行類(增刪改)
                //{
                //    //1.增加
                //    int result0 = db.Database.ExecuteSqlRaw("insert into T_SysErrorLog(id,userId,userAccount) values('44','11111','test1')");
                //    int result1 = db.Database.ExecuteSqlRaw("insert into T_SysErrorLog(id,userId,userAccount) values('55','11111','test1')");

                //    //2. 修改
                //    MySqlParameter[] paras ={
                //                                     new MySqlParameter("@id","1"),
                //                                     new MySqlParameter("@userAccount","未知"),
                //                                };
                //    int result2 = db.Database.ExecuteSqlRaw("update T_SysErrorLog set userAccount=@userAccount where id=@id", paras);

                //    //3. 刪除
                //    var myId = "44";
                //    int result3 = db.Database.ExecuteSqlRaw($"delete from T_SysErrorLog where id={myId}");

                //    //4. 其它指令
                //    int result4 = db.Database.ExecuteSqlRaw("truncate table T_SysLoginLog");
                //}
                #endregion
View Code

4. 調用存儲過程

  待補充,后續結合存儲過程章節一起補充

5. 事務

(1).SaveChanges:經測試SaveChanges事務一體是好用的,但是下面關閉默認事務無效!!.

  db.Database.AutoTransactionsEnabled = false;

(2). DbContextTransaction:適用場景多次savechanges+SQL語句調用、多種數據庫鏈接技術(EFCore和ADO.Net)

 A.場景多次savechanges+SQL語句調用:經測試,可以正常使用。

 B. 場景多種數據庫鏈接技術(EFCore和ADO.Net):存在一個事務類型轉換bug,暫時未沒有解決.

(3). 環境事務(TransactionScope)

 A.多個SaveChange+SQL場景:經測試,沒問題。

 B.多種數據庫鏈接技術(EFCore和ADO.Net)場景:存在一個事務類型轉換bug,暫時未沒有解決。

 C.多個EF上下鏈接同一個數據庫:經測試,沒問題。

代碼分享: 

              #region 01-SaveChange事務
                //{
                //    try
                //    {

                //        for (int i = 0; i < 5; i++)
                //        {
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //        }
                //        //模擬失敗
                //        baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N")+"1", addTime = DateTime.Now });  //模擬失敗
                //        int count = baseService.SaveChange();
                //    }
                //    catch (Exception ex)
                //    {
                //        Console.WriteLine(ex.Message);
                //    }

                //}
                #endregion

                #region 02-DbContextTransaction(多個SaveChange)
                //{
                //    using (var transaction = db.Database.BeginTransaction())
                //    {
                //        //using包裹,catch中可以不用寫rollback,自動回滾
                //        try
                //        {
                //            //1. 業務1
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //            baseService.SaveChange();

                //            //2. 業務2
                //            db.Database.ExecuteSqlRaw($"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111','test1')");

                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //統一提交
                //            transaction.Commit();
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);                     
                //        }
                //    }

                //}
                #endregion

                #region 02-DbContextTransaction(多種數據庫技術)--有bug
                //{
                //    var conStr = @"Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456";
                //    using (var connection = new MySql.Data.MySqlClient.MySqlConnection(conStr))
                //    {
                //        connection.Open();
                //        using (var transaction = db.Database.BeginTransaction())
                //        {
                //            try
                //            {
                //                //1. ADO.Net
                //                var command = connection.CreateCommand();
                //                command.Transaction = (MySqlTransaction)transaction;
                //                command.CommandText = $"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111111111','test1')";
                //                command.ExecuteNonQuery();

                //                //2. EF Core
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "000000000000", addTime = DateTime.Now });
                //                baseService.SaveChange();


                //                //3.模擬失敗
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //                baseService.SaveChange();


                //                //綜合提交
                //                transaction.Commit();

                //            }
                //            catch (Exception ex)
                //            {
                //                Console.WriteLine(ex.Message);
                //            }
                //        }

                //    }

                //}
                #endregion

                #region 03-TransactionScope(多個SaveChange+SQL)
                //{
                //    using (var transaction = new TransactionScope())
                //    {
                //        //using包裹,catch中可以不用寫rollback,自動回滾
                //        try
                //        {
                //            //1. 業務1
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //            baseService.SaveChange();

                //            //2. 業務2
                //            db.Database.ExecuteSqlRaw($"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111','test1')");

                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //統一提交
                //            transaction.Complete();
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);
                //        }
                //    }

                //}
                #endregion

                #region 03-TransactionScope(多種數據庫技術)--有bug
                //{
                //    var conStr = @"Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456";
                //    using (var connection = new MySql.Data.MySqlClient.MySqlConnection(conStr))
                //    {
                //        connection.Open();
                //        using (var transaction = new TransactionScope())
                //        {
                //            try
                //            {
                //                //1. ADO.Net
                //                var command = connection.CreateCommand();
                //                command.Transaction = (MySqlTransaction)transaction;
                //                command.CommandText = $"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111111111','test1')";
                //                command.ExecuteNonQuery();

                //                //2. EF Core
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "000000000000", addTime = DateTime.Now });
                //                baseService.SaveChange();


                //                //3.模擬失敗
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //                baseService.SaveChange();


                //                //綜合提交
                //                transaction.Complete();

                //            }
                //            catch (Exception ex)
                //            {
                //                Console.WriteLine(ex.Message);
                //            }
                //        }

                //    }

                //}
                #endregion

                #region 03-(同一個數據庫不同上下)
                //{
                //    using (var scope = new TransactionScope())
                //    {
                //        try
                //        {
                //            //1.業務1
                //            using (var context = new CoreFrameDBContext())
                //            {
                //                context.Add(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //                context.SaveChanges();
                //            }
                //            //2.業務2
                //            using (var context = new CoreFrameDBContext2())
                //            {
                //                context.Add(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //                context.SaveChanges();
                //            }
                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //綜合提交
                //            scope.Complete();

                //            Console.WriteLine("成功了");
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);
                //        }
                //    }

                //}
                #endregion
View Code

6. 性能測試 

 結論:以上測試得出來一個結論,EFCore處理增刪改在10000條數據以內速度還是可以接受的,並且EFCore調用SQL語句組裝並不能提升性能,反而下降明顯!!

 代碼分享:

             #region 01-增加(EFCore)
                //{
                //    for (int i = 0; i < 10; i++)
                //    {
                //        T_SysErrorLog sErrorLog = new T_SysErrorLog();
                //        sErrorLog.id = Guid.NewGuid().ToString("N");
                //        sErrorLog.userId = "001";
                //        sErrorLog.userAccount = "12345";
                //        sErrorLog.logLevel = "Error";
                //        sErrorLog.logMessage = "出錯了";
                //        sErrorLog.addTime = DateTime.Now;
                //        sErrorLog.delFlag = 0;
                //        baseService.AddNo(sErrorLog);
                //    }
                //    int count = baseService.SaveChange();
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 01-增加(EFCore調用SQL)
                //{
                //    string sqlStr = "";
                //    for (int i = 0; i < 1000; i++)
                //    {
                //        sqlStr = sqlStr + $"insert into T_SysErrorLog values('{Guid.NewGuid().ToString("N")}', '001', '12345','Error','出錯了','{DateTime.Now}',0);";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 02-修改(EFCore)
                //{
                //    //先用上面的增加語句添加指定條目的數據

                //    var list = baseService.GetListBy<T_SysErrorLog>(u => u.id != "000");
                //    foreach (var item in list)
                //    {
                //        item.logLevel = "ERROR1110";
                //        item.logMessage = "出錯了2220";
                //        item.addTime = DateTime.Now;
                //    }
                //    int count = baseService.SaveChange();

                //}
                #endregion

                #region 02-修改(EFCore調用SQL)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    var list = baseService.GetListBy<T_SysErrorLog>(u => u.id != "000");
                //    string sqlStr = "";
                //    foreach (var item in list)
                //    {
                //        sqlStr = sqlStr + $"update T_SysErrorLog set logLevel='ERROR110',logMessage='出錯了220',addTime='{DateTime.Now}' where id='{item.id}';";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 03-刪除(EFCore)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    int count = baseService.DelBy<T_SysErrorLog>(u => u.id != "fk");

                //}
                #endregion

                #region 03-刪除(EFCore調用SQL)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    var list = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "000").Select(u => u.id).ToList();
                //    string sqlStr = "";
                //    foreach (var item in list)
                //    {
                //        sqlStr = sqlStr + $"delete from T_SysErrorLog where id='{item}';";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion
View Code

7. 性能優化

(1).SqlBulkCopy:基於 System.Data.SqlClient ,僅支持SQLServer, Pass掉。

(2).EFCore.BulkExtensions:僅支持SQLServer 和 SQLite,Pass掉  【該組件已收費,不再使用】

(3).Z.EntityFramework.Plus.EFCore:

A.說明:免費, 支持MySQL,,且目前已經支持EFCore5.x版本了, 但功能有限, 僅支持:Batch Delete、Batch Update. (刪除和修改)

 GitHub地址:https://github.com/zzzprojects/EntityFramework-Plus

 官網文檔地址:http://entityframework-plus.net/batch-delete 

 

注意:更強大的BulkSaveChanges、 BulkInsert、 BulkUpdate、BulkDelete、BulkMerge 對應收費的程序集 Z.EntityFramework.Extensions (收費!!!)

B.性能測試:

C. 測試是否支持事務:

 經測試,支持Transaction事務的統一提交和回滾。

最后總結:目前只找到大數據刪除和修改的組件,增加的組件目前沒有找到!!!

代碼分享:

   {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                Console.WriteLine("開始執行。。。。。。");

                #region 01-刪除
                //{
                //    int count1 = db.T_SysErrorLog.Where(u => u.id != "1").Delete();
                //    //db.T_SysErrorLog.Where(u => u.id != "1").Delete(x => x.BatchSize = 1000);
                //} 
                #endregion

                #region 02-修改
                //{
                //    int count1 = db.T_SysErrorLog.Where(u => u.id != "1").Update(x=>new T_SysErrorLog() { logLevel="Error33324",logMessage="出3錯4342了",addTime=DateTime.Now});

                //} 
                #endregion

                #region 03-測試事務
                {
                    using (var transaction = db.Database.BeginTransaction())
                    {
                        BaseService baseService1 = new BaseService(db);
                        //using包裹,不需要手動寫rollback
                        try
                        {
                            //1.普通增加
                            for (int i = 0; i < 5; i++)
                            {
                                baseService.AddNo(new T_SysErrorLog() { id = i.ToString(), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                            }
                            baseService.SaveChange();

                            //2. 組件的刪除
                            db.T_SysErrorLog.Where(u => u.id == "1").Delete();

                            //3. 組件的更新
                            db.T_SysErrorLog.Where(u => u.id != "0001").Update(x => new T_SysErrorLog() { logLevel = "Error33324", logMessage = "出3錯4342了", addTime = DateTime.Now });

                            //4. 模擬失敗
                            baseService1.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                            baseService1.SaveChange();

                            //5.最后提交
                            transaction.Commit();

                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);

                        }
                    }

                }
                #endregion



                Console.WriteLine("執行完成");
                watch.Stop();
                Console.WriteLine($"時間為:{watch.ElapsedMilliseconds}ms");
            }
View Code

PS:上述代碼事務的時候必須是同一個db

8. 並發測試

(1). 監測單個字段: [ConcurrencyCheck] 或 entity.Property(p => p.age).IsConcurrencyToken();

 配置T_Test表中的age4字段,進行測試, 有效可以使用。

(2). 監測整條數據:[Timestamp]或entity.Property(e => e.rowVersion).IsRowVersion();

 配置T_Test表新增timestamp類型的rowVersion字段,實體中進行上述配置,進行測試, 發現無效不能使用!!!

代碼分享:

   #region 01-單字段監控
                //{
                //    CoreFrameDBContext db1 = new CoreFrameDBContext();
                //    CoreFrameDBContext db2 = new CoreFrameDBContext();
                //    try
                //    {
                //        var data1 = db1.T_Test.Where(u => u.id == 1).FirstOrDefault();
                //        var data2 = db2.T_Test.Where(u => u.id ==1).FirstOrDefault();

                //        data1.age4 = data1.age4 - 2;
                //        int result1 = db1.SaveChanges();

                //        data2.age4 = data2.age4 - 4;
                //        int result2 = db2.SaveChanges();  //發現age的值和原先查出來的不一致,會拋異常進入cache
                //    }
                //    catch (DbUpdateConcurrencyException ex)
                //    {

                //        var entityEntry = ex.Entries.Single();
                //        var original = entityEntry.OriginalValues.ToObject() as T_Test; //數據庫原始值  10
                //        var database = entityEntry.GetDatabaseValues().ToObject() as T_Test; //數據庫現在值 8
                //        var current = entityEntry.CurrentValues.ToObject() as T_Test; //當前內存值 6
                //        entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體

                //        current.age4 = database.age4 - 4;  //應該拿着當前數據庫實際的值去處理,即8-4=4
                //        entityEntry.CurrentValues.SetValues(current);
                //        int result3 = db2.SaveChanges();
                //    }
                //}
                #endregion

                #region 02-全字段監控
                //{
                //    CoreFrameDBContext db1 = new CoreFrameDBContext();
                //    CoreFrameDBContext db2 = new CoreFrameDBContext();
                //    try
                //    {
                //        var data1 = db1.T_Test.Where(u => u.id == 1).FirstOrDefault();
                //        var data2 = db2.T_Test.Where(u => u.id == 1).FirstOrDefault();

                //        data1.age4 = data1.age4 - 2;
                //        int result1 = db1.SaveChanges();

                //        data2.age4 = data2.age4 - 4;
                //        int result2 = db2.SaveChanges();  //發現age的值和原先查出來的不一致,會拋異常進入cache
                //    }
                //    catch (DbUpdateConcurrencyException ex)
                //    {

                //        var entityEntry = ex.Entries.Single();
                //        var original = entityEntry.OriginalValues.ToObject() as T_Test; //數據庫原始值  10
                //        var database = entityEntry.GetDatabaseValues().ToObject() as T_Test; //數據庫現在值 8
                //        var current = entityEntry.CurrentValues.ToObject() as T_Test; //當前內存值 6
                //        entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體

                //        current.age4 = database.age4 - 4;  //應該拿着當前數據庫實際的值去處理,即8-4=4
                //        entityEntry.CurrentValues.SetValues(current);
                //        int result3 = db2.SaveChanges();
                //    }
                //}
                #endregion
View Code

9. 索引映射

 給T_Test表中的name1添加索引,age1和age2添加聯合索引,通過指令映射,發現索引映射成功。

    [Index(nameof(age1), nameof(age2), Name = "ids_age")]
    [Index(nameof(name1), Name = "ids_name1")]
    public partial class T_Test{}

 

四. 實戰測試2 

前言:

 以下測試都基於【Microsoft.EntityFrameworkCore】來進行,連接字符串如下:

 optionsBuilder.UseMySQL("Server=xxxx;Database=CoreFrameDB;User ID=root;Password=xxxx;");

1. 基礎CRUD(+封裝)

  經測試,均可正常使用。

封裝方法:

 /// <summary>
    /// 泛型方法,直接注入EF上下文
    /// </summary>
    public class BaseService { 
        public DbContext db;

        /// <summary>
        /// 在使用的時候,自動注入db上下文
        /// </summary>
        /// <param name="db"></param>
        public BaseService(DbContext db)
        {
            this.db = db;

            //關閉全局追蹤的代碼
            //db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        }

        /****************************************下面進行方法的封裝(同步)***********************************************/
        //1. 直接提交數據庫

        #region 01-數據源
        public IQueryable<T> Entities<T>() where T : class
        {
            return db.Set<T>();
        }

        public IQueryable<T> EntitiesNoTrack<T>() where T : class
        {
            return db.Set<T>().AsNoTracking();
        }

        #endregion

        #region 02-新增
        public int Add<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Added;
            return db.SaveChanges();

        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        /// <returns></returns>
        public int Del<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
            return db.SaveChanges();
        }
        #endregion

        #region 04-根據條件刪除(支持批量刪除)
        /// <summary>
        /// 根據條件刪除(支持批量刪除)
        /// </summary>
        /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param>
        /// <returns></returns>
        public int DelBy<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = db.Set<T>().Where(delWhere).ToList();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
            return db.SaveChanges();
        }
        #endregion

        #region 05-單實體修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        /// <returns></returns>
        public int Modify<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
            return db.SaveChanges();
        }
        #endregion

        #region 06-批量修改(非lambda)
        /// <summary>
        /// 批量修改(非lambda)
        /// </summary>
        /// <param name="model">要修改實體中 修改后的屬性 </param>
        /// <param name="whereLambda">查詢實體的條件</param>
        /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
        /// <returns></returns>
        public int ModifyBy<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class
        {
            List<T> listModifes = db.Set<T>().Where(whereLambda).ToList();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>
            {
                if (proNames.Contains(p.Name))
                {
                    dicPros.Add(p.Name, p);
                }
            });
            foreach (string proName in proNames)
            {
                if (dicPros.ContainsKey(proName))
                {
                    PropertyInfo proInfo = dicPros[proName];
                    object newValue = proInfo.GetValue(model, null);
                    foreach (T m in listModifes)
                    {
                        proInfo.SetValue(m, newValue, null);
                    }
                }
            }
            return db.SaveChanges();
        }
        #endregion

        #region 07-根據條件查詢
        /// <summary>
        /// 根據條件查詢
        /// </summary>
        /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetListBy<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class
        {
            if (isTrack)
            {
                return db.Set<T>().Where(whereLambda).ToList();
            }
            else
            {
                return db.Set<T>().Where(whereLambda).AsNoTracking().ToList();
            }
           
        }
        #endregion

        #region 08-根據條件排序和查詢
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetListBy<T, Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            IQueryable<T> data = null;         
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda);
            }
            else
            {
                data = data.OrderByDescending(orderLambda);
            }
            return data.ToList();
        }
        #endregion

        #region 09-分頁查詢(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageList<T, Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {

            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            return data.ToList();
        }
        #endregion

        #region 10-分頁查詢(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageListByName<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            List<T> list = null;
            if (isTrack)
            {
                list = db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            else
            {
                list = db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            return list;
        }
        #endregion

        #region 11-分頁查詢輸出總行數(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageList<T, Tkey>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            int count = db.Set<T>().Where(whereLambda).Count();
            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data=data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            rowCount = count;
            return data.ToList();
        }
        #endregion

        #region 12-分頁查詢輸出總行數(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public List<T> GetPageListByName<T>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            int count = 0;
            count = db.Set<T>().Where(whereLambda).Count();
            List<T> list = null;
            if (isTrack)
            {
                list= db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
            }
            else
            {
                list = db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                   .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            }
            rowCount = count;
            return list;
        }
        #endregion


        //2. SaveChange剝離出來,處理事務

        #region 01-批量處理SaveChange()
        /// <summary>
        /// 事務批量處理
        /// </summary>
        /// <returns></returns>
        public int SaveChange()
        {
            return db.SaveChanges();
        }
        #endregion

        #region 02-新增
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="model">需要新增的實體</param>
        public void AddNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Added;
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        public void DelNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
        }
        #endregion

        #region 04-根據條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public void DelByNo<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = db.Set<T>().Where(delWhere).ToList();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
        }
        #endregion

        #region 05-修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        public void ModifyNo<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
        }
        #endregion


        //3. EF調用sql語句

        #region 01-執行增加,刪除,修改操作(或調用相關存儲過程)
        /// <summary>
        /// 執行增加,刪除,修改操作(或調用存儲過程)
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public int ExecuteSql(string sql, params SqlParameter[] pars)
        {
            return db.Database.ExecuteSqlRaw(sql, pars);
        }

        #endregion

        #region 02-執行查詢操作(調用查詢類的存儲過程)
        /// <summary>
        /// 執行查詢操作
        /// 注:查詢必須返回實體的所有屬性字段;結果集中列名必須與屬性映射的項目匹配;查詢中不能包含關聯數據
        /// 除Select以外其他的SQL語句無法執行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> ExecuteQuery<T>(string sql, bool isTrack = true, params SqlParameter[] pars) where T : class
        {
            if (isTrack)
            {
                //表示跟蹤狀態(默認是跟蹤的)
                return db.Set<T>().FromSqlRaw(sql, pars).ToList();
            }
            else
            {
                //表示不跟蹤狀態
                return db.Set<T>().FromSqlRaw(sql, pars).AsNoTracking().ToList();
            }
        }
        #endregion

        #region 03-執行查詢操作(與Linq相結合)
        /// <summary>
        /// 執行查詢操作
        /// 注:查詢必須返回實體的所有屬性字段;結果集中列名必須與屬性映射的項目匹配;查詢中不能包含關聯數據
        /// 除Select以外其他的SQL語句無法執行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        ///  <param name="whereLambda">查詢條件</param>
        /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> ExecuteQueryWhere<T>(string sql, Expression<Func<T, bool>> whereLambda, bool isTrack = true, params SqlParameter[] pars) where T : class
        {
            if (isTrack)
            {
                //表示跟蹤狀態(默認是跟蹤的)
                return db.Set<T>().FromSqlRaw(sql, pars).Where(whereLambda).ToList();
            }
            else
            {
                //表示不跟蹤狀態
                return db.Set<T>().FromSqlRaw(sql, pars).Where(whereLambda).AsNoTracking().ToList();
            }
        }
        #endregion



        /****************************************下面進行方法的封裝(異步)***********************************************/

        #region 01-新增
        public async Task<int> AddAsync<T>(T model) where T : class
        {
            await db.AddAsync(model);
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 02-刪除
        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="model">需要刪除的實體</param>
        /// <returns></returns>
        public async Task<int> DelAsync<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Deleted;
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 03-根據條件刪除(支持批量刪除)
        /// <summary>
        /// 根據條件刪除(支持批量刪除)
        /// </summary>
        /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param>
        /// <returns></returns>
        public async Task<int> DelByAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels = await db.Set<T>().Where(delWhere).ToListAsync();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 04-單實體修改
        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model">修改后的實體</param>
        /// <returns></returns>
        public async Task<int> ModifyAsync<T>(T model) where T : class
        {
            db.Entry(model).State = EntityState.Modified;
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 05-批量修改(非lambda)
        /// <summary>
        /// 批量修改(非lambda)
        /// </summary>
        /// <param name="model">要修改實體中 修改后的屬性 </param>
        /// <param name="whereLambda">查詢實體的條件</param>
        /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
        /// <returns></returns>
        public async Task<int> ModifyByAsync<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class
        {
            List<T> listModifes = await db.Set<T>().Where(whereLambda).ToListAsync();
            Type t = typeof(T);
            List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>();
            proInfos.ForEach(p =>
            {
                if (proNames.Contains(p.Name))
                {
                    dicPros.Add(p.Name, p);
                }
            });
            foreach (string proName in proNames)
            {
                if (dicPros.ContainsKey(proName))
                {
                    PropertyInfo proInfo = dicPros[proName];
                    object newValue = proInfo.GetValue(model, null);
                    foreach (T m in listModifes)
                    {
                        proInfo.SetValue(m, newValue, null);
                    }
                }
            }
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 06-根據條件查詢
        /// <summary>
        /// 根據條件查詢
        /// </summary>
        /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetListByAsync<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class
        {
            if (isTrack)
            {
                return await db.Set<T>().Where(whereLambda).ToListAsync();
            }
            else
            {
                return await db.Set<T>().Where(whereLambda).AsNoTracking().ToListAsync();
            }

        }
        #endregion

        #region 07-根據條件排序和查詢
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetListByAsync<T, Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {
            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda);
            }
            else
            {
                data = data.OrderByDescending(orderLambda);
            }
            return await data.ToListAsync();
        }
        #endregion

        #region 08-分頁查詢(根據Lambda排序)
        /// <summary>
        /// 根據條件排序和查詢
        /// </summary>
        /// <typeparam name="Tkey">排序字段類型</typeparam>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="pageSize">頁容量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="orderLambda">排序條件</param>
        /// <param name="isAsc">升序or降序</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>>  GetPageListAsync<T, Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class
        {

            IQueryable<T> data = null;
            if (isTrack)
            {
                data = db.Set<T>().Where(whereLambda);
            }
            else
            {
                data = db.Set<T>().Where(whereLambda).AsNoTracking();
            }
            if (isAsc)
            {
                data = data.OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            else
            {
                data = data.OrderByDescending(orderLambda).Skip((pageIndex - 1) * pageSize).Take(pageSize);
            }
            return await data.ToListAsync();
        }
        #endregion

        #region 09-分頁查詢(根據名稱排序)
        /// <summary>
        /// 分頁查詢輸出總行數(根據名稱排序)
        /// </summary>
        /// <param name="pageIndex">頁碼</param>
        /// <param name="rowCount">輸出的總數量</param>
        /// <param name="whereLambda">查詢條件</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">asc 或 desc</param>
        ///  <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param>
        /// <returns></returns>
        public async Task<List<T>> GetPageListByNameAsync<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class
        {
            List<T> list = null;
            if (isTrack)
            {
                list = await db.Set<T>().Where(whereLambda).DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            }
            else
            {
                list = await db.Set<T>().Where(whereLambda).AsNoTracking().DataSorting(sortName, sortDirection)
                 .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            }
            return list;
        }
        #endregion

 

        //2. SaveChange剝離出來,處理事務

        #region 01-批量處理SaveChange()
        /// <summary>
        /// 事務批量處理
        /// </summary>
        /// <returns></returns>
        public async Task<int> SaveChangeAsync()
        {
            return await db.SaveChangesAsync();
        }
        #endregion

        #region 02-新增
        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="model">需要新增的實體</param>
        public async Task<EntityEntry<T>> AddNoAsync<T>(T model) where T : class
        {
           return await db.AddAsync(model);
        }
        #endregion
        
        #region 03-根據條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public async Task DelByNoAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            List<T> listDels =await db.Set<T>().Where(delWhere).ToListAsync();
            listDels.ForEach(model =>
            {
                db.Entry(model).State = EntityState.Deleted;
            });
        }
        #endregion


        //3. EF調用sql語句

        #region 01-執行增加,刪除,修改操作(或調用存儲過程)
        /// <summary>
        /// 執行增加,刪除,修改操作(或調用存儲過程)
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public async Task<int> ExecuteSqlAsync(string sql, params SqlParameter[] pars)
        {
            return await db.Database.ExecuteSqlRawAsync(sql, pars);
        }
        #endregion


        /****************************************下面是基於【EFCore.BulkExtensions】大數據的處理 (同步)***********************************************/

        #region 01-增加
        /// <summary>
        /// 增加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkInsert<T>(List<T> list) where T : class
        {
            db.BulkInsert<T>(list);
        }
        #endregion

        #region 02-修改
        /// <summary>
        /// 修改
        /// PS:傳入的實體如果不賦值,則更新為null,即傳入的實體每個字段都要有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkUpdate<T>(List<T> list) where T : class
        {
            db.BulkUpdate<T>(list);
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// PS:傳入的list中的實體僅需要主鍵有值,它是根據主鍵刪除的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public void BulkDelete<T>(List<T> list) where T : class
        {
            db.BulkDelete<T>(list);
        }
        #endregion

        #region 04-條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public int BatchDelete<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            return db.Set<T>().Where(delWhere).BatchDelete();
        }
        #endregion

        #region 05-條件更新1
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public int BatchUpdate<T>(Expression<Func<T, bool>> delWhere, T model) where T : class, new()
        {
            return db.Set<T>().Where(delWhere).BatchUpdate(model);
        }
        #endregion

        #region 06-條件更新2
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public int BatchUpdate2<T>(Expression<Func<T, bool>> delWhere, Expression<Func<T, T>> modelWhere) where T : class, new()
        {
            return db.Set<T>().Where(delWhere).BatchUpdate(modelWhere);
        }
        #endregion


        /****************************************下面是基於【EFCore.BulkExtensions】大數據的處理 (異步)***********************************************/

        #region 01-增加
        /// <summary>
        /// 增加
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkInsertAsync<T>(List<T> list) where T : class
        {
            await db.BulkInsertAsync<T>(list);
        }
        #endregion

        #region 02-修改
        /// <summary>
        /// 修改
        /// PS:傳入的實體如果不賦值,則更新為null,即傳入的實體每個字段都要有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkUpdateAsync<T>(List<T> list) where T : class
        {
            await db.BulkUpdateAsync<T>(list);
        }
        #endregion

        #region 03-刪除
        /// <summary>
        /// 刪除
        /// PS:傳入的list中的實體僅需要主鍵有值,它是根據主鍵刪除的
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        public async void BulkDeleteAsync<T>(List<T> list) where T : class
        {
            await db.BulkDeleteAsync<T>(list);
        }
        #endregion

        #region 04-條件刪除
        /// <summary>
        /// 條件刪除
        /// </summary>
        /// <param name="delWhere">需要刪除的條件</param>
        public async Task<int> BatchDeleteAsync<T>(Expression<Func<T, bool>> delWhere) where T : class
        {
            return await db.Set<T>().Where(delWhere).BatchDeleteAsync();
        }
        #endregion

        #region 05-條件更新1
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public async  Task<int> BatchUpdateAsync<T>(Expression<Func<T, bool>> delWhere, T model) where T : class, new()
        {
            return await db.Set<T>().Where(delWhere).BatchUpdateAsync(model);
        }
        #endregion

        #region 06-條件更新2
        /// <summary>
        /// 條件更新
        /// PS:要更新哪幾個字段,就給傳入的實體中的哪幾個字段賦值
        /// </summary>
        /// <param name="delWhere">需要更新的條件</param>
        /// <param name="model">更新為的實體</param>
        public async Task<int> BatchUpdate2Async<T>(Expression<Func<T, bool>> delWhere, Expression<Func<T, T>> modelWhere) where T : class, new()
        {
            return await db.Set<T>().Where(delWhere).BatchUpdateAsync(modelWhere);
        }
        #endregion

    }
 /// <summary>
    /// 排序的擴展
    /// </summary>
    public static class SortExtension
    {

        #region 01-根據string名稱排序擴展(單字段)
        /// <summary>
        /// 根據string名稱排序擴展(單字段)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">排序數據源</param>
        /// <param name="sortName">排序名稱</param>
        /// <param name="sortDirection">排序方式 asc或desc</param>
        /// <returns></returns>
        public static IQueryable<T> DataSorting<T>(this IQueryable<T> source, string sortName, string sortDirection)
        {
            string sortingDir = string.Empty;
            if (sortDirection.ToUpper().Trim() == "ASC")
            {
                sortingDir = "OrderBy";
            }
            else if (sortDirection.ToUpper().Trim() == "DESC")
            {
                sortingDir = "OrderByDescending";
            }
            ParameterExpression param = Expression.Parameter(typeof(T), sortName);
            PropertyInfo pi = typeof(T).GetProperty(sortName);
            Type[] types = new Type[2];
            types[0] = typeof(T);
            types[1] = pi.PropertyType;
            Expression expr = Expression.Call(typeof(Queryable), sortingDir, types, source.Expression, Expression.Lambda(Expression.Property(param, sortName), param));
            IQueryable<T> query = source.AsQueryable().Provider.CreateQuery<T>(expr);
            return query;
        }
        #endregion

        #region 02-根據多個string名稱排序擴展(多字段)
        /// <summary>
        ///  根據多個string名稱排序擴展(多字段)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">數據源</param>
        /// <param name="orderParams">排序類</param>
        /// <returns></returns>
        public static IQueryable<T> DataManySorting<T>(this IQueryable<T> data, params FiledOrderParam[] orderParams) where T : class
        {
            var parameter = Expression.Parameter(typeof(T), "p");
            if (orderParams != null && orderParams.Length > 0)
            {
                for (int i = 0; i < orderParams.Length; i++)
                {
                    var property = typeof(T).GetProperty(orderParams[i].PropertyName);
                    if (property != null)
                    {
                        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                        var orderByExpr = Expression.Lambda(propertyAccess, parameter);
                        string methodName = i > 0 ?
                            orderParams[i].IsDesc ? "ThenByDescending" : "ThenBy"
                            : orderParams[i].IsDesc ? "OrderByDescending" : "OrderBy";
                        var resultExp = Expression.Call(
                            typeof(Queryable), methodName,
                            new Type[] { typeof(T), property.PropertyType },
                            data.Expression, Expression.Quote(orderByExpr)
                            );
                        data = data.Provider.CreateQuery<T>(resultExp);
                    }
                }
            }
            return data;
        }

        #endregion
    }


    /// <summary>
    /// 排序類
    /// </summary>
    public class FiledOrderParam
    {
        //是否降序
        public bool IsDesc { get; set; }
        //排序名稱
        public string PropertyName { get; set; }
    }
View Code

測試代碼: 

               #region 01-增加
                {
                    //T_Test tTest = new T_Test()
                    //{
                    //    age1 = 1,
                    //    age2 = 2,
                    //    age3 = 3,
                    //    age4 = 4,
                    //    age5 = 5,
                    //    money1 = (float)10.12,
                    //    money2 = 20.34,
                    //    money3 = (decimal)12.13,
                    //    addTime1 = 2020,
                    //    addTime2 = DateTime.Now,
                    //    addTime3 = TimeSpan.FromMinutes(10),
                    //    addTime4 = DateTime.Now,
                    //    addTime5 = DateTime.Now,
                    //    name1 = "ypf1",
                    //    name2 = "ypf2",
                    //    name3 = "ypf3",
                    //    name4 = "ypf4",
                    //    name5 = "ypf5",
                    //    name6 = "ypf6",
                    //    isSex1 = 1
                    //};
                    //int count1 = baseService.Add(tTest);

                    //T_SysErrorLog sErrorLog = new T_SysErrorLog();
                    //sErrorLog.id = Guid.NewGuid().ToString("N");
                    //sErrorLog.userId = "001";
                    //sErrorLog.userAccount = "12345";
                    //sErrorLog.logLevel = "Error";
                    //sErrorLog.logMessage = "出錯了";
                    //sErrorLog.addTime = DateTime.Now;
                    //sErrorLog.delFlag = 0;
                    //int count2 = baseService.Add(sErrorLog);
                }
                #endregion

                #region 02-修改
                //{
                //    var data = baseService.Entities<T_SysErrorLog>().Where(u => u.id == "1").FirstOrDefault();
                //    data.userAccount = "123456";
                //    baseService.SaveChange();
                //    Console.WriteLine("修改成功");
                //}
                #endregion

                #region 03-刪除
                //{
                //    baseService.DelBy<T_SysErrorLog>(u => u.id != "1");
                //    Console.WriteLine("刪除成功");
                //}
                #endregion

                #region 04-根據條件查詢和排序
                //{
                //    var list = baseService.GetListBy<T_SysErrorLog, DateTime?>(u => u.id != "xxx", p => p.addTime, false);
                //    foreach (var item in list)
                //    {
                //        Console.WriteLine($"id={item.id},userId={item.userId},userAccount={item.userAccount},addTime={item.addTime}");
                //    }

                //} 
                #endregion

                #region 05-根據字段名稱升/降序分頁查詢
                //{
                //    int pageIndex = 1;
                //    int pageSize = 2;
                //    //1.分開寫法
                //    var list1 = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "fk").DataSorting("addTime", "desc").Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                //    //2. 封裝調用
                //    int count = 0;
                //    var list2 = baseService.GetPageListByName<T_SysErrorLog>(pageIndex, pageSize, out count, u => u.id != "fk", "addTime", "desc");

                //    //3.多字段排序
                //    FiledOrderParam[] param = {
                //        new FiledOrderParam(){IsDesc=false,PropertyName="addTime"},
                //        new FiledOrderParam(){IsDesc=true,PropertyName="id"}
                //    };
                //    var list3 = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "fk").DataManySorting(param).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                //}
                #endregion
View Code

2. 各種Linq測試

 詳細測試了各種linq對應MySQL的翻譯,代碼如下:

                #region 01-First/Last/Single/ElementAt
                //{
                //    //說明:First/FirstOrDefault 翻譯limit 1, Last/LastOrDefault不能翻譯  Single/SingleOrDefault翻譯成limit 2, ElementAt/ElementAtOrDefault不能翻譯

                //    db.T_SysErrorLog.First();
                //    db.T_SysErrorLog.FirstOrDefault();

                //    db.T_SysErrorLog.Last();
                //    db.T_SysErrorLog.LastOrDefault();

                //    var d1 = db.T_SysErrorLog.Single();
                //    var d2 = db.T_SysErrorLog.SingleOrDefault();

                //    var d3 = db.T_SysErrorLog.ElementAt(1);
                //    var d4 = db.T_SysErrorLog.ElementAtOrDefault(1);

                //}
                #endregion

                #region 02-Select相關
                //{
                //    //正常翻譯select
                //    var d1 = (from a in db.T_SysErrorLog
                //              select a.logLevel).ToList();
                //    //翻譯出來別名和計算
                //    var d2 = (from a in db.T_SysErrorLog
                //              select new
                //              {
                //                  a.logLevel,
                //                  a.addTime,
                //                  myMsg = a.logMessage,
                //                  myflag = a.delFlag / 2
                //              }).ToList();

                //    //這種嵌套無法翻譯,報錯
                //    var d3 = (from a in db.T_SysErrorLog
                //              select new
                //              {
                //                  a.logLevel,
                //                  a.addTime,
                //                  myMsg = from b in db.T_SysUser
                //                          select b.userAccount,
                //                  myflag = a.delFlag / 2
                //              }).ToList();

                //}
                #endregion

                #region 03-基本函數
                {
                    //1.以下基礎函數都可以翻譯成對應的mysql中的函數
                    //db.T_SysErrorLog.Count();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Sum();
                    //db.T_SysErrorLog.Sum(o => o.delFlag);
                    //db.T_SysErrorLog.Select(o => o.delFlag).Max();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Min();
                    //db.T_SysErrorLog.Select(o => o.delFlag).Average();



                }
                #endregion

                #region 04-關聯查詢
                //{

                //    //隱式內連接 翻譯成 cross join (在mysql中,join、cross join、inner join含義相同)
                //    var data1 = (from a in db.T_SysUser
                //                 from b in db.T_SysLoginLog
                //                 where a.id == b.userId
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     b.loginCity,
                //                     b.loginIp,
                //                     b.loginTime
                //                 }).ToList();

                //    //顯式內連接 翻譯成inner join 
                //    var data2 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     b.loginCity,
                //                     b.loginIp,
                //                     b.loginTime
                //                 }).ToList();

                //    //外鏈接翻譯成 left join (linq中通過顛倒數據位置實現left 或 right join)
                //    var data3 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId into fk
                //                 from c in fk.DefaultIfEmpty()
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     c.loginCity,
                //                     c.loginIp,
                //                     c.loginTime
                //                 }).ToList();

                //    //統計右表的數量,報錯,翻譯不出來!!
                //    var data4 = (from a in db.T_SysUser
                //                 join b in db.T_SysLoginLog
                //                 on a.id equals b.userId into fk
                //                 select new
                //                 {
                //                     a.userAccount,
                //                     a.userPhone,
                //                     myCount = fk.Count()
                //                 }).ToList();


                //}
                #endregion

                #region 05-排序
                //{
                //    //升序和降序 正常翻譯 mysql的排序
                //    var d1 = db.T_SysErrorLog.OrderBy(u => u.addTime).ThenByDescending(u => u.delFlag).ToList();
                //    var d2 = (from a in db.T_SysErrorLog
                //              orderby a.addTime, a.delFlag descending
                //              select a).ToList();
                //}
                #endregion

                #region 06-分組
                //{

                //    //前提:必須加.AsEnumerable(),否則報錯
                //    //以下經測試均可以使用,但是翻譯的sql語句不顯示group
                //    var d1 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by a.delFlag into g
                //              select g).ToList();

                //    var d2 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by a.delFlag into g
                //              select new
                //              {
                //                  myKey = g.Key,
                //                  g
                //              }).ToList();

                //    var d3 = (from a in db.T_SysErrorLog.AsEnumerable()
                //              group a by new { myFlag = a.delFlag != 2 } into g
                //              select new
                //              {
                //                  myKey = g.Key,
                //                  g
                //              }).ToList();
                //}
                #endregion

                #region 07-分頁
                //{
                //    //翻譯成mysql的 limit+offset用法,注意不是單獨的limit用法
                //    var d1 = db.T_SysErrorLog.Skip(2).Take(10).ToList();

                //    //下面兩句都報錯,無法翻譯
                //    var d2 = db.T_SysErrorLog.SkipWhile(u => u.delFlag ==0).ToList();
                //    var d3 = db.T_SysErrorLog.TakeWhile(u => u.delFlag == 0).ToList();
                //}
                #endregion

                #region 08-Contains/EF.Functions.Like/Concat/Union/Intersect/Except
                //{
                //    //1. 這里成Contains翻譯到mysql中的in
                //    string[] myList = { "222", "333", "444" };
                //    string mystr = "222,333,444";
                //    var d1 = db.T_SysErrorLog.Where(u => myList.Contains(u.logLevel)).ToList();
                //    // 這里的contians翻譯成 LIKE '%222%'     (sqlserver翻譯成charindex)
                //    var d2 = db.T_SysErrorLog.Where(u => u.logLevel.Contains("222")).ToList();
                //    //這里的contians翻譯成 LOCATE
                //    //補充:locate(subStr,string) :函數返回subStr在string中出現的位置
                //    var d3 = db.T_SysErrorLog.Where(u => mystr.Contains(u.logLevel)).ToList();

                //    //2. 翻譯成 LIKE '%222%
                //    var d4 = db.T_SysErrorLog.Where(u => EF.Functions.Like(u.logLevel, "%222%")).ToList();

                //    //3. 翻譯成Union All 不去重
                //    var d5 = ((from a in db.T_SysUser select a.id)
                //           .Concat
                //           (from a in db.T_SysRole select a.id)).ToList();
                //    //翻譯成Union 去重
                //    var d6 = ((from a in db.T_SysUser select a.id)
                //        .Union
                //        (from a in db.T_SysRole select a.id)).ToList();
                //    //無法翻譯報錯
                //    var d7 = ((from a in db.T_SysUser select a.id)
                //        .Intersect
                //        (from a in db.T_SysRole select a.id)).ToList();

                //    //無法翻譯報錯
                //    var d8= ((from a in db.T_SysUser select a.id)
                //        .Except
                //        (from a in db.T_SysRole select a.id)).ToList();


                //}
                #endregion

                #region 09-DateTime/String部分方法
                //{
                //    //翻譯成 EXTRACT方法
                //    var d1 = (from a in db.T_SysErrorLog
                //             where a.addTime.Value.Year == 2019
                //             select a).ToList();
                //    //翻譯成 Like
                //    var d2 = (from a in db.T_SysErrorLog
                //              where a.logLevel.StartsWith("333")
                //              select a).ToList();

                //    //翻譯成 SUBSTRING
                //    var d3 = (from a in db.T_SysErrorLog
                //             select a.logLevel.Substring(0,5)).ToList();
                //}
                #endregion
View Code

3. 調用SQL語句

  經測試,可以正常使用。需要注意的是參數化查詢要用 MySqlParameter。

代碼分享:

 #region 01-查詢類(很雞肋,只能單表全部查詢,不能指定字段)
                //{
                //    //1.基本的原生SQL查詢
                //    var userList1 = db.Set<T_SysErrorLog>().FromSqlRaw("select * from T_SysErrorLog where id!='123'").ToList();

                //    //2.利用$內插語法進行傳遞
                //    var myId = "1";
                //    var userList2 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog where id= {myId}").ToList();

                //    //3.原生SQL與linq語法相結合
                //    var userList3 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog")
                //        .Where(u => u.id == "2")
                //        .ToList();
                //    var userList4 = db.Set<T_SysErrorLog>().FromSqlRaw($"select * from T_SysErrorLog")
                //        .Where(u => u.id != "1111")
                //        .OrderBy(u => u.addTime)
                //        .ToList();

                //    //4.利用SqlParameter進行參數化查詢 MySql.Data.MySqlClient.MySqlParameter
                //    MySqlParameter[] paras ={
                //                             new MySqlParameter("@id","2fc343069e0a4a559b62b08d5999dbcd"),
                //                             new MySqlParameter("@userAccount","ypf"),
                //                        };
                //    var userList5 = db.Set<T_SysErrorLog>().FromSqlRaw("select * from T_SysErrorLog where id=@id and userAccount=@userAccount", paras).ToList();

                //} 
                #endregion

                #region 02-執行類(增刪改)
                //{
                //    //1.增加
                //    int result0 = db.Database.ExecuteSqlRaw("insert into T_SysErrorLog(id,userId,userAccount) values('44','11111','test1')");
                //    int result1 = db.Database.ExecuteSqlRaw("insert into T_SysErrorLog(id,userId,userAccount) values('55','11111','test1')");

                //    //2. 修改
                //    MySqlParameter[] paras ={
                //                                     new MySqlParameter("@id","1"),
                //                                     new MySqlParameter("@userAccount","未知"),
                //                                };
                //    int result2 = db.Database.ExecuteSqlRaw("update T_SysErrorLog set userAccount=@userAccount where id=@id", paras);

                //    //3. 刪除
                //    var myId = "44";
                //    int result3 = db.Database.ExecuteSqlRaw($"delete from T_SysErrorLog where id={myId}");

                //    //4. 其它指令
                //    int result4 = db.Database.ExecuteSqlRaw("truncate table T_SysLoginLog");
                //}
                #endregion
View Code

4. 調用存儲過程

  待補充,后續結合存儲過程章節一起補充

5. 事務

(1).SaveChanges:經測試SaveChanges事務一體是好用的,但是下面關閉默認事務無效!!.

  db.Database.AutoTransactionsEnabled = false;

(2). DbContextTransaction:適用場景多次savechanges+SQL語句調用、多種數據庫鏈接技術(EFCore和ADO.Net)

 A.場景多次savechanges+SQL語句調用:經測試,可以正常使用。

 B. 場景多種數據庫鏈接技術(EFCore和ADO.Net):存在一個事務類型轉換bug,暫時未沒有解決.

(3). 環境事務(TransactionScope)

 A.多個SaveChange+SQL場景:經測試,沒問題。

 B.多種數據庫鏈接技術(EFCore和ADO.Net)場景:存在一個事務類型轉換bug,暫時未沒有解決。

 C.多個EF上下鏈接同一個數據庫:經測試,沒問題。

代碼分享: 

              #region 01-SaveChange事務
                //{
                //    try
                //    {

                //        for (int i = 0; i < 5; i++)
                //        {
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //        }
                //        //模擬失敗
                //        baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N")+"1", addTime = DateTime.Now });  //模擬失敗
                //        int count = baseService.SaveChange();
                //    }
                //    catch (Exception ex)
                //    {
                //        Console.WriteLine(ex.Message);
                //    }

                //}
                #endregion

                #region 02-DbContextTransaction(多個SaveChange)
                //{
                //    using (var transaction = db.Database.BeginTransaction())
                //    {
                //        //using包裹,catch中可以不用寫rollback,自動回滾
                //        try
                //        {
                //            //1. 業務1
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //            baseService.SaveChange();

                //            //2. 業務2
                //            db.Database.ExecuteSqlRaw($"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111','test1')");

                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //統一提交
                //            transaction.Commit();
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);                     
                //        }
                //    }

                //}
                #endregion

                #region 02-DbContextTransaction(多種數據庫技術)--有bug
                //{
                //    var conStr = @"Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456";
                //    using (var connection = new MySql.Data.MySqlClient.MySqlConnection(conStr))
                //    {
                //        connection.Open();
                //        using (var transaction = db.Database.BeginTransaction())
                //        {
                //            try
                //            {
                //                //1. ADO.Net
                //                var command = connection.CreateCommand();
                //                command.Transaction = (MySqlTransaction)transaction;
                //                command.CommandText = $"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111111111','test1')";
                //                command.ExecuteNonQuery();

                //                //2. EF Core
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "000000000000", addTime = DateTime.Now });
                //                baseService.SaveChange();


                //                //3.模擬失敗
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //                baseService.SaveChange();


                //                //綜合提交
                //                transaction.Commit();

                //            }
                //            catch (Exception ex)
                //            {
                //                Console.WriteLine(ex.Message);
                //            }
                //        }

                //    }

                //}
                #endregion

                #region 03-TransactionScope(多個SaveChange+SQL)
                //{
                //    using (var transaction = new TransactionScope())
                //    {
                //        //using包裹,catch中可以不用寫rollback,自動回滾
                //        try
                //        {
                //            //1. 業務1
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //            baseService.SaveChange();

                //            //2. 業務2
                //            db.Database.ExecuteSqlRaw($"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111','test1')");

                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //統一提交
                //            transaction.Complete();
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);
                //        }
                //    }

                //}
                #endregion

                #region 03-TransactionScope(多種數據庫技術)--有bug
                //{
                //    var conStr = @"Server=localhost;Database=CoreFrameDB;User ID=root;Password=123456";
                //    using (var connection = new MySql.Data.MySqlClient.MySqlConnection(conStr))
                //    {
                //        connection.Open();
                //        using (var transaction = new TransactionScope())
                //        {
                //            try
                //            {
                //                //1. ADO.Net
                //                var command = connection.CreateCommand();
                //                command.Transaction = (MySqlTransaction)transaction;
                //                command.CommandText = $"insert into T_SysErrorLog(id,userId,userAccount) values('{Guid.NewGuid().ToString("N")}','11111111111','test1')";
                //                command.ExecuteNonQuery();

                //                //2. EF Core
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "000000000000", addTime = DateTime.Now });
                //                baseService.SaveChange();


                //                //3.模擬失敗
                //                baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //                baseService.SaveChange();


                //                //綜合提交
                //                transaction.Complete();

                //            }
                //            catch (Exception ex)
                //            {
                //                Console.WriteLine(ex.Message);
                //            }
                //        }

                //    }

                //}
                #endregion

                #region 03-(同一個數據庫不同上下)
                //{
                //    using (var scope = new TransactionScope())
                //    {
                //        try
                //        {
                //            //1.業務1
                //            using (var context = new CoreFrameDBContext())
                //            {
                //                context.Add(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //                context.SaveChanges();
                //            }
                //            //2.業務2
                //            using (var context = new CoreFrameDBContext2())
                //            {
                //                context.Add(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                //                context.SaveChanges();
                //            }
                //            //3.模擬失敗
                //            baseService.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                //            baseService.SaveChange();

                //            //綜合提交
                //            scope.Complete();

                //            Console.WriteLine("成功了");
                //        }
                //        catch (Exception ex)
                //        {
                //            Console.WriteLine(ex.Message);
                //        }
                //    }

                //}
                #endregion
View Code

6. 性能測試 

 結論:以上測試得出來一個結論,EFCore處理增刪改在10000條數據以內速度還是可以接受的,並且EFCore調用SQL語句組裝並不能提升性能,反而下降明顯!!

 代碼分享:

             #region 01-增加(EFCore)
                //{
                //    for (int i = 0; i < 10; i++)
                //    {
                //        T_SysErrorLog sErrorLog = new T_SysErrorLog();
                //        sErrorLog.id = Guid.NewGuid().ToString("N");
                //        sErrorLog.userId = "001";
                //        sErrorLog.userAccount = "12345";
                //        sErrorLog.logLevel = "Error";
                //        sErrorLog.logMessage = "出錯了";
                //        sErrorLog.addTime = DateTime.Now;
                //        sErrorLog.delFlag = 0;
                //        baseService.AddNo(sErrorLog);
                //    }
                //    int count = baseService.SaveChange();
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 01-增加(EFCore調用SQL)
                //{
                //    string sqlStr = "";
                //    for (int i = 0; i < 1000; i++)
                //    {
                //        sqlStr = sqlStr + $"insert into T_SysErrorLog values('{Guid.NewGuid().ToString("N")}', '001', '12345','Error','出錯了','{DateTime.Now}',0);";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 02-修改(EFCore)
                //{
                //    //先用上面的增加語句添加指定條目的數據

                //    var list = baseService.GetListBy<T_SysErrorLog>(u => u.id != "000");
                //    foreach (var item in list)
                //    {
                //        item.logLevel = "ERROR1110";
                //        item.logMessage = "出錯了2220";
                //        item.addTime = DateTime.Now;
                //    }
                //    int count = baseService.SaveChange();

                //}
                #endregion

                #region 02-修改(EFCore調用SQL)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    var list = baseService.GetListBy<T_SysErrorLog>(u => u.id != "000");
                //    string sqlStr = "";
                //    foreach (var item in list)
                //    {
                //        sqlStr = sqlStr + $"update T_SysErrorLog set logLevel='ERROR110',logMessage='出錯了220',addTime='{DateTime.Now}' where id='{item.id}';";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion

                #region 03-刪除(EFCore)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    int count = baseService.DelBy<T_SysErrorLog>(u => u.id != "fk");

                //}
                #endregion

                #region 03-刪除(EFCore調用SQL)
                //{
                //    //先用上面的增加語句添加指定條目的數據
                //    var list = baseService.Entities<T_SysErrorLog>().Where(u => u.id != "000").Select(u => u.id).ToList();
                //    string sqlStr = "";
                //    foreach (var item in list)
                //    {
                //        sqlStr = sqlStr + $"delete from T_SysErrorLog where id='{item}';";
                //    }
                //    int count = db.Database.ExecuteSqlRaw(sqlStr);
                //    Console.WriteLine("執行成功");
                //}
                #endregion
View Code

7. 性能優化

(1).SqlBulkCopy:基於 System.Data.SqlClient ,僅支持SQLServer, Pass掉。

(2).EFCore.BulkExtensions:僅支持SQLServer 和 SQLite,Pass掉  【該組件已收費,不再使用】

(3).Z.EntityFramework.Plus.EFCore:

A.說明:免費, 支持MySQL,,且目前已經支持EFCore5.x版本了, 但功能有限, 僅支持:Batch Delete、Batch Update. (刪除和修改)

 GitHub地址:https://github.com/zzzprojects/EntityFramework-Plus

 官網文檔地址:http://entityframework-plus.net/batch-delete 

 

注意:更強大的BulkSaveChanges、 BulkInsert、 BulkUpdate、BulkDelete、BulkMerge 對應收費的程序集 Z.EntityFramework.Extensions (收費!!!)

B.性能測試:

C. 測試是否支持事務:

 經測試,支持Transaction事務的統一提交和回滾。

最后總結:目前只找到大數據刪除和修改的組件,增加的組件目前沒有找到!!!

代碼分享:

   {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                Console.WriteLine("開始執行。。。。。。");

                #region 01-刪除
                //{
                //    int count1 = db.T_SysErrorLog.Where(u => u.id != "1").Delete();
                //    //db.T_SysErrorLog.Where(u => u.id != "1").Delete(x => x.BatchSize = 1000);
                //} 
                #endregion

                #region 02-修改
                //{
                //    int count1 = db.T_SysErrorLog.Where(u => u.id != "1").Update(x=>new T_SysErrorLog() { logLevel="Error33324",logMessage="出3錯4342了",addTime=DateTime.Now});

                //} 
                #endregion

                #region 03-測試事務
                {
                    using (var transaction = db.Database.BeginTransaction())
                    {
                        BaseService baseService1 = new BaseService(db);
                        //using包裹,不需要手動寫rollback
                        try
                        {
                            //1.普通增加
                            for (int i = 0; i < 5; i++)
                            {
                                baseService.AddNo(new T_SysErrorLog() { id = i.ToString(), userId = Guid.NewGuid().ToString("N"), addTime = DateTime.Now });
                            }
                            baseService.SaveChange();

                            //2. 組件的刪除
                            db.T_SysErrorLog.Where(u => u.id == "1").Delete();

                            //3. 組件的更新
                            db.T_SysErrorLog.Where(u => u.id != "0001").Update(x => new T_SysErrorLog() { logLevel = "Error33324", logMessage = "出3錯4342了", addTime = DateTime.Now });

                            //4. 模擬失敗
                            baseService1.AddNo(new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = Guid.NewGuid().ToString("N") + "1", addTime = DateTime.Now });  //模擬失敗
                            baseService1.SaveChange();

                            //5.最后提交
                            transaction.Commit();

                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);

                        }
                    }

                }
                #endregion



                Console.WriteLine("執行完成");
                watch.Stop();
                Console.WriteLine($"時間為:{watch.ElapsedMilliseconds}ms");
            }
View Code

PS:上述代碼事務的時候必須是同一個db

8. 並發測試

(1). 監測單個字段: [ConcurrencyCheck] 或 entity.Property(p => p.age).IsConcurrencyToken();

 配置T_Test表中的age4字段,進行測試, 有效可以使用。

(2). 監測整條數據:[Timestamp]或entity.Property(e => e.rowVersion).IsRowVersion();

 配置T_Test表新增timestamp類型的rowVersion字段,實體中進行上述配置,進行測試, 發現無效不能使用!!!

代碼分享:

   #region 01-單字段監控
                //{
                //    CoreFrameDBContext db1 = new CoreFrameDBContext();
                //    CoreFrameDBContext db2 = new CoreFrameDBContext();
                //    try
                //    {
                //        var data1 = db1.T_Test.Where(u => u.id == 1).FirstOrDefault();
                //        var data2 = db2.T_Test.Where(u => u.id ==1).FirstOrDefault();

                //        data1.age4 = data1.age4 - 2;
                //        int result1 = db1.SaveChanges();

                //        data2.age4 = data2.age4 - 4;
                //        int result2 = db2.SaveChanges();  //發現age的值和原先查出來的不一致,會拋異常進入cache
                //    }
                //    catch (DbUpdateConcurrencyException ex)
                //    {

                //        var entityEntry = ex.Entries.Single();
                //        var original = entityEntry.OriginalValues.ToObject() as T_Test; //數據庫原始值  10
                //        var database = entityEntry.GetDatabaseValues().ToObject() as T_Test; //數據庫現在值 8
                //        var current = entityEntry.CurrentValues.ToObject() as T_Test; //當前內存值 6
                //        entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體

                //        current.age4 = database.age4 - 4;  //應該拿着當前數據庫實際的值去處理,即8-4=4
                //        entityEntry.CurrentValues.SetValues(current);
                //        int result3 = db2.SaveChanges();
                //    }
                //}
                #endregion

                #region 02-全字段監控
                //{
                //    CoreFrameDBContext db1 = new CoreFrameDBContext();
                //    CoreFrameDBContext db2 = new CoreFrameDBContext();
                //    try
                //    {
                //        var data1 = db1.T_Test.Where(u => u.id == 1).FirstOrDefault();
                //        var data2 = db2.T_Test.Where(u => u.id == 1).FirstOrDefault();

                //        data1.age4 = data1.age4 - 2;
                //        int result1 = db1.SaveChanges();

                //        data2.age4 = data2.age4 - 4;
                //        int result2 = db2.SaveChanges();  //發現age的值和原先查出來的不一致,會拋異常進入cache
                //    }
                //    catch (DbUpdateConcurrencyException ex)
                //    {

                //        var entityEntry = ex.Entries.Single();
                //        var original = entityEntry.OriginalValues.ToObject() as T_Test; //數據庫原始值  10
                //        var database = entityEntry.GetDatabaseValues().ToObject() as T_Test; //數據庫現在值 8
                //        var current = entityEntry.CurrentValues.ToObject() as T_Test; //當前內存值 6
                //        entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體

                //        current.age4 = database.age4 - 4;  //應該拿着當前數據庫實際的值去處理,即8-4=4
                //        entityEntry.CurrentValues.SetValues(current);
                //        int result3 = db2.SaveChanges();
                //    }
                //}
                #endregion
View Code

9. 索引映射

 給T_Test表中的name1添加索引,age1和age2添加聯合索引,通過指令映射,發現索引映射成功。

    [Index(nameof(age1), nameof(age2), Name = "ids_age")]
    [Index(nameof(name1), Name = "ids_name1")]
    public partial class T_Test{}

 

 

 

 

 

 

 

 

!

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

 


免責聲明!

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



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