一. 结构介绍
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.AdminWebinDebug ,以便后续与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路径的问题:开发环境 需要有 inDebug etcoreapp3.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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。