一. 說明
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; } }
測試代碼:

#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
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
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
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
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
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"); }
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
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; } }
測試代碼:

#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
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
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
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
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
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"); }
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
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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。