一. 結構介紹
1. 分層建項目
新建:YpfCore.AdminWeb、YpfCore.Data、YpfCore.DTO、YpfCore.IService、YpfCore.Service、YpfCore.Utils,每層的作用如下:
A. YpfCore.AdminWeb層:UI層,存放一些頁面和進行一些基本的業務邏輯,供客戶端調用。
B. YpfCore.Data層:數據層,存放數據庫實體映射類和相關配置類、EF上下文類。
C. YpfCore.DTO層:數據傳輸對象層,存放一些業務邏輯實體,供UI層調用。
D. YpfCore.IService層:業務接口層。
E. YpfCore.Service層:業務層。
F. YpfCore.Utils層:幫助類層
(后續補充 YpfCore.WebApi層,用於前后端分離的接口編寫)。
2. 項目結構圖
二. 搭建步驟
1. 數據層構建
(1).通過Nuget給YpfCore.Data層安裝EFCore相關的程序集,如下:
【Microsoft.EntityFrameworkCore】【Microsoft.EntityFrameworkCore.SqlServer】【Microsoft.EntityFrameworkCore.Design】【Microsoft.EntityFrameworkCore.Tools】
PS: 這里安裝的是 3.1.8 版本,此處搭建適宜SQLServer為例演示,MySQL相關集成后續擴展封裝章節會體現。
(2).通過下面指令映射數據庫實體文件,這里采用注解即DataAnnotations進行關系格式映射。
【Scaffold-DbContext "Server=47.92.xxx.xxx;Database=CoreFrameDB;User ID=CoreFrameDB;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -Context CoreFrameDBContext -UseDatabaseNames -DataAnnotations】
(3). 為了便於后續業務代碼的編寫,這里我們添加日志,用於打印Linq 轉變成的 SQL語句。
通過Nuget安裝日志程序集:【Microsoft.Extensions.Logging】【Microsoft.Extensions.Logging.Debug】,修改EFCore上下問中的代碼如下:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLoggerFactory(LoggerFactory.Create(build => { build.AddDebug(); })); }
2. 業務接口層構建
(1). 給YpfCore.IService層添加對YpfCore.Data層的引用,同時通過Nuget安裝如下程序集:
【Microsoft.EntityFrameworkCore】【System.Data.SqlClient】
(2). 新增IBaseService 和 ISupport接口,IBaseService用於定義EFCore上下文對DB操作的方法約束,ISupport為了標記后續哪些子類Service可以被注入到YpfCore.AdminWeb層。
IBaseService代碼分享

public interface IBaseService { /****************************************下面進行方法的封裝(同步)***********************************************/ //1. 直接提交數據庫 #region 01-數據源 IQueryable<T> Entities<T>() where T : class; IQueryable<T> EntitiesNoTrack<T>() where T : class; #endregion #region 02-新增 int Add<T>(T model) where T : class; #endregion #region 03-刪除 /// <summary> /// 刪除 /// </summary> /// <param name="model">需要刪除的實體</param> /// <returns></returns> int Del<T>(T model) where T : class; #endregion #region 04-根據條件刪除(支持批量刪除) /// <summary> /// 根據條件刪除(支持批量刪除) /// </summary> /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param> /// <returns></returns> int DelBy<T>(Expression<Func<T, bool>> delWhere) where T : class; #endregion #region 05-單實體修改 /// <summary> /// 修改 /// </summary> /// <param name="model">修改后的實體</param> /// <returns></returns> int Modify<T>(T model) where T : class; #endregion #region 06-批量修改(非lambda) /// <summary> /// 批量修改(非lambda) /// </summary> /// <param name="model">要修改實體中 修改后的屬性 </param> /// <param name="whereLambda">查詢實體的條件</param> /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param> /// <returns></returns> int ModifyBy<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class; #endregion #region 07-根據條件查詢 /// <summary> /// 根據條件查詢 /// </summary> /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param> /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param> /// <returns></returns> List<T> GetListBy<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class; #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> List<T> GetListBy<T, Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true, bool isTrack = true) where T : class; #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> 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; #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> List<T> GetPageListByName<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class; #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> 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; #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> 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; #endregion //2. SaveChange剝離出來,處理事務 #region 01-批量處理SaveChange() /// <summary> /// 事務批量處理 /// </summary> /// <returns></returns> int SaveChange(); #endregion #region 02-新增 /// <summary> /// 新增 /// </summary> /// <param name="model">需要新增的實體</param> void AddNo<T>(T model) where T : class; #endregion #region 03-刪除 /// <summary> /// 刪除 /// </summary> /// <param name="model">需要刪除的實體</param> void DelNo<T>(T model) where T : class; #endregion #region 04-根據條件刪除 /// <summary> /// 條件刪除 /// </summary> /// <param name="delWhere">需要刪除的條件</param> void DelByNo<T>(Expression<Func<T, bool>> delWhere) where T : class; #endregion #region 05-修改 /// <summary> /// 修改 /// </summary> /// <param name="model">修改后的實體</param> void ModifyNo<T>(T model) where T : class; #endregion //3. EF調用sql語句 #region 01-執行增加,刪除,修改操作(或調用存儲過程) /// <summary> /// 執行增加,刪除,修改操作(或調用存儲過程) /// </summary> /// <param name="sql"></param> /// <param name="pars"></param> /// <returns></returns> int ExecuteSql(string sql, params SqlParameter[] pars); #endregion #region 02-執行查詢操作 /// <summary> /// 執行查詢操作 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sql"></param> /// <param name="pars"></param> /// <returns></returns> List<T> ExecuteQuery<T>(string sql, bool isTrack = true, params SqlParameter[] pars) where T : class; #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> List<T> ExecuteQueryWhere<T>(string sql, Expression<Func<T, bool>> whereLambda, bool isTrack = true, params SqlParameter[] pars) where T : class; #endregion /****************************************下面進行方法的封裝(異步)***********************************************/ //1. 直接提交數據庫 #region 01-新增 Task<int> AddAsync<T>(T model) where T : class; #endregion #region 02-刪除 /// <summary> /// 刪除 /// </summary> /// <param name="model">需要刪除的實體</param> /// <returns></returns> Task<int> DelAsync<T>(T model) where T : class; #endregion #region 03-根據條件刪除(支持批量刪除) /// <summary> /// 根據條件刪除(支持批量刪除) /// </summary> /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param> /// <returns></returns> Task<int> DelByAsync<T>(Expression<Func<T, bool>> delWhere) where T : class; #endregion #region 04-單實體修改 /// <summary> /// 修改 /// </summary> /// <param name="model">修改后的實體</param> /// <returns></returns> Task<int> ModifyAsync<T>(T model) where T : class; #endregion #region 05-批量修改(非lambda) /// <summary> /// 批量修改(非lambda) /// </summary> /// <param name="model">要修改實體中 修改后的屬性 </param> /// <param name="whereLambda">查詢實體的條件</param> /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param> /// <returns></returns> Task<int> ModifyByAsync<T>(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) where T : class; #endregion #region 06-根據條件查詢 /// <summary> /// 根據條件查詢 /// </summary> /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param> /// <param name="isTrack">是否跟蹤狀態,默認是跟蹤的</param> /// <returns></returns> Task<List<T>> GetListByAsync<T>(Expression<Func<T, bool>> whereLambda, bool isTrack = true) where T : class; #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> 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; #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> 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; #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> Task<List<T>> GetPageListByNameAsync<T>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, string sortName, string sortDirection, bool isTrack = true) where T : class; #endregion //2. SaveChange剝離出來,處理事務 #region 01-批量處理SaveChange() /// <summary> /// 事務批量處理 /// </summary> /// <returns></returns> Task<int> SaveChangeAsync(); #endregion #region 02-新增 /// <summary> /// 新增 /// </summary> /// <param name="model">需要新增的實體</param> Task<EntityEntry<T>> AddNoAsync<T>(T model) where T : class; #endregion #region 03-根據條件刪除 /// <summary> /// 條件刪除 /// </summary> /// <param name="delWhere">需要刪除的條件</param> Task DelByNoAsync<T>(Expression<Func<T, bool>> delWhere) where T : class; #endregion //3. EF調用sql語句 #region 01-執行增加,刪除,修改操作(或調用存儲過程) /// <summary> /// 執行增加,刪除,修改操作(或調用存儲過程) /// </summary> /// <param name="sql"></param> /// <param name="pars"></param> /// <returns></returns> Task<int> ExecuteSqlAsync(string sql, params SqlParameter[] pars); #endregion }
ISupport代碼分享

/// <summary> /// 一個標記接口,只有實現該接口的類才進行注入 /// </summary> public interface ISupport { }
3. 業務層構建
(1).給YpfCore.Service層添加對 YpfCore.Data、YpfCore.IServie、Ypf.Uitls層的引用,同時通過Nuget安裝如下程序集:
【Microsoft.EntityFrameworkCore】【Microsoft.EntityFrameworkCore.SqlServer】
(2).新增BaseService類,實現了IBaseService 和 ISupport接口,主要是EFCore對DB增刪改查各種操作封裝。
BaseService代碼分享:

/// <summary> /// 泛型方法,直接注入EF上下文 /// </summary> public class BaseService : IBaseService, ISupport { public DbContext db; /// <summary> /// 在使用的時候,自動注入db上下文 /// </summary> /// <param name="db"></param> public BaseService(CoreFrameDBContext db) { this.db = db; //關閉全局追蹤的代碼 //db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } /****************************************下面EFCore基礎方法的封裝(同步)***********************************************/ //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 /****************************************下面EFCore基礎方法的封裝(異步)***********************************************/ #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 }
(3).將程序集的輸出路徑改為:..\YpfCore.AdminWeb\bin\Debug\ ,以便后續與YpfCore.AdminWeb層解耦。
分析:這里BaseService采用的是泛型方法而不是泛型類 。
好處:
在子類Service中,想操控哪張表直接傳入表對應的實體即可,相對靈活。如果用泛型類,子類在實例化的時候已經決定了T的內容,不便於靈活調用各張表。
4. 幫助類層構建
暫無必須內容
5. DTO層構建
暫無必須內容
6. UI層構建
(1).給YpfCore.AdminWeb層添加對YpfCore.Data、YpfCore.IServie、Ypf.Uitls、YpfCore.DTO層的引用, 同時通過Nuget安裝如下程序集:
【Autofac 6.0.0】【Autofac.Extensions.DependencyInjection 7.0.2】
(2). 在Startup中的ConfigureService中添加EFCore上下文的注入,默認使用請求內單例的注入:
services.AddDbContext<CoreFrameDBContext>(option => option.UseSqlServer(_Configuration.GetConnectionString("EFStr")), ServiceLifetime.Scoped);
(3).通過AutoFac把YpfCore.Service.dll中所有實現ISupport的接口的(非抽象)類都注冊給實現他的全部接口,且支持在Asp.Net Core中以作用域單例的形式實現構造函數注入。
A. ConfigureService添加的代碼
/// <summary> /// 在這個方法中注冊業務,他在ConfigureService后執行 /// </summary> /// <param name="builder"></param> public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterModule<DefaultModule>(); }
B. Auto封裝類
/// <summary> /// 服務於AutoFac /// </summary> public class DefaultModule : Module { protected override void Load(ContainerBuilder builder) { { //這里就是AutoFac的注入方式,下面采用常規的方式 //詳見:https://www.cnblogs.com/yaopengfei/p/9479268.html //官網:https://autofac.org/ //特別注意:其中很大的一個變化在於,Autofac 原來的一個生命周期InstancePerRequest,將不再有效。正如我們前面所說的,整個request的生命周期被ASP.NET Core管理了, //所以Autofac的這個將不再有效。我們可以使用 InstancePerLifetimeScope ,同樣是有用的,對應了我們ASP.NET Core DI 里面的Scoped。 //關於dll路徑的問題:開發環境 需要有 \bin\Debug\netcoreapp3.1\, 而生產環境不需要, 使用AppContext.BaseDirectory來獲取根目錄恰好符合該要求。 //在普通類中配置文件的讀取麻煩,后面封裝(注:appsettings.json要改為始終復制) var Configuration = new ConfigurationBuilder().AddJsonFile(AppContext.BaseDirectory + "appsettings.json").Build(); var dirName = Configuration["IocDll"]; Assembly asmService = Assembly.LoadFile(AppContext.BaseDirectory + dirName); builder.RegisterAssemblyTypes(asmService) .Where(t => !t.IsAbstract && typeof(ISupport).IsAssignableFrom(t)) //只有實現了ISupport接口的類才進行注冊 .AsImplementedInterfaces() //把一個類注冊給它實現的全部接口 .InstancePerLifetimeScope() //作用域單例(比如Task.Run就是另外一個作用域),而非請求內單例(請求內單例用:InstancePerRequest) .PropertiesAutowired(); //在core里表示在注入類中實現構造函數注入 } } }
配置文件
{
"IocDll": "YpfCore.Service.dll"
}
C. Program中的代碼
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //Core3.0 后,AutoFac的用法 .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
PS:這里也可以使用原生反射和Core中自帶的注冊實現,不過AutoFac的功能更加豐富一些。
截止此處,一個最基本的簡單架構已經完成了。
三. 基本測試
1.調用模式1
在YpfCore.AdminWeb層的控制器中注入IBaseService,操控哪張表,調用方法的時候傳入對應表的實體類即可。各種同步、異步的封裝方法詳見BaseService。
代碼分享:
public void Test3([FromServices] IBaseService _myBaseService) { //查詢 var data1 = _myBaseService.Entities<T_SysUser>().Where(u => u.id != "1").ToList(); var data2 = _myBaseService.GetListBy<T_SysOperLog>(u => u.id != "1"); //刪除 var count1 = _myBaseService.DelBy<T_SysLoginLog>(u => u.id != "1"); }
2. 調用模式2
分別在YpfCore.IService和YpfCore.Service中編寫子類接口ITest1Service和子類Test1Service,Test1Service需要繼承 BaseService, ITest1Service, ISupport,其中ISupport用來標記可以被AutoFac反射。
ITest1Service代碼:
public interface ITest1Service { //測試將業務寫到子Service中封裝 int Test1(); int Test2(); }
Test1Service代碼:
public class Test1Service : BaseService, ITest1Service, ISupport {public Test1Service(CoreFrameDBContext db) : base(db) { } #region 04-測試將業務寫到子Service中封裝 /// <summary> /// 基本操作(推薦用法) /// </summary> /// <returns></returns> public int Test1() { var data3 = this.GetListBy<T_SysLoginLog>(u => u.id != "ddd"); var data4 = this.GetListBy<T_SysPermisson>(u => u.id != "ddd"); T_SysErrorLog s1 = new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "111", addTime = DateTime.Now }; T_SysLoginLog s2 = new T_SysLoginLog() { id = Guid.NewGuid().ToString("N"), userId = "111", loginTime = DateTime.Now }; this.AddNo(s1); this.AddNo(s2); int result2 = this.SaveChange(); return 1; } /// <summary> /// SaveChanges事務操作 /// </summary> /// <returns></returns> public int Test2() {//方案二:直接用父類的db (推薦用法) { T_SysErrorLog s1 = new T_SysErrorLog() { id = Guid.NewGuid().ToString("N"), userId = "111", addTime = DateTime.Now }; T_SysLoginLog s2 = new T_SysLoginLog() { id = Guid.NewGuid().ToString("N"), userId = "111", loginTime = DateTime.Now }; db.Add(s1); db.Add(s2); db.SaveChanges(); } return 111; } #endregion }
控制器中注入,調用代碼
public void Test4([FromServices] ITest1Service _test1Service) { _test1Service.Test1(); _test1Service.Test2(); }
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。