前言
.net core已经出来一段时间了,相信大家对.net core的概念已经很清楚了,这里就不再赘述。笔者目前也用.net core做过一些项目,并且将以前framework下的一些经验移植到了.net core下,并结合.net core本身的一些特性整理成此框架,以供学习参考。如有不足之处,欢迎指正。
先睹为快,演示地址:http://cloud.eggtwo.com/main/index
框架介绍
先来一张整体分层结构图
基础层
1.Cloud.Core项目是核心项目,主要实现缓存的操作、dapper操作、EF Repository、PageList、日志等操作
2.Cloud.Utility属于帮助类
领域层
3.Cloud.Entity实体对象,存放数据库映射实体、Fluent API配置、枚举字典、DbContext等
4.Cloud.UnitOfWork,操作数据库的网关,里面封装了对仓储的操作、dapper的操作、事务等
服务层
5.Cloud.Service 业务逻辑的实现
6.Cloud.Dto 数据传输对象,实体对象不直接和表现层接触,通过dto互转
表现层
7.Cloud.Framework,表现层框架,封装了超类controller,全局授权过滤器,全局异常过滤器,ActionFilter,HtmlHelper等操作
8.Cloud.Boss 启动项目
使用的技术
基于.net core 2.0的asp.net core mvc
基于.net core 2.0的ef
dapper
mysql
前端框架 aceAdmin
技术要点
1.实体基类定义
2.泛型仓储的封装
2.1仓储接口的定义,泛型约束T必须是BaseEntity类型
public interface IRepository<T> where T : BaseEntity { DatabaseFacade Database { get; } IQueryable<T> Entities { get; } int SaveChanges(); Task<int> SaveChangesAsync(); void Disposed(); bool Delete(List<T> entitys, bool isSaveChange = true); bool Delete(T entity, bool isSaveChange = true); Task<bool> DeleteAsync(List<T> entitys, bool isSaveChange = true); Task<bool> DeleteAsync(T entity, bool isSaveChange = true); Task<T> GetAsync(Expression<Func<T, bool>> predicate = null); Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate = null); T Get(object id); T Get(Expression<Func<T, bool>> predicate = null); Task<T> GetAsync(object id); Task<IQueryable<T>> LoadAsync(Expression<Func<T, bool>> predicate = null); bool Insert(List<T> entitys, bool isSaveChange = true); bool Insert(T entity, bool isSaveChange = true); Task<bool> InsertAsync(List<T> entitys, bool isSaveChange = true); Task<bool> InsertAsync(T entity, bool isSaveChange = true); bool Update(List<T> entitys, bool isSaveChange = true); bool Update(T entity, bool isSaveChange = true, List<string> updatePropertyList = null); Task<bool> UpdateAsync(List<T> entitys, bool isSaveChange = true); Task<bool> UpdateAsync(T entity, bool isSaveChange = true, List<string> updatePropertyList = null); }
2.2仓储接口的实现
public class Repository<T> : IRepository<T> where T : BaseEntity { DbContext _dbContext; public Repository(DbContext dbContext) { _dbContext = dbContext; } public int SaveChanges() { return _dbContext.SaveChanges(); } public async Task<int> SaveChangesAsync() { return await _dbContext.SaveChangesAsync(); } public void Disposed() { throw new Exception("不允许在这里释放上下文,请在UnitOfWork中操作"); _dbContext.Dispose(); } #region 插入数据 public bool Insert(T entity, bool isSaveChange = true) { _dbContext.Set<T>().Add(entity); if (isSaveChange) { return SaveChanges() > 0; } return false; } public async Task<bool> InsertAsync(T entity, bool isSaveChange = true) { _dbContext.Set<T>().Add(entity); if (isSaveChange) { return await SaveChangesAsync() > 0; } return false; } public bool Insert(List<T> entitys, bool isSaveChange = true) { _dbContext.Set<T>().AddRange(entitys); if (isSaveChange) { return SaveChanges() > 0; } return false; } public async Task<bool> InsertAsync(List<T> entitys, bool isSaveChange = true) { _dbContext.Set<T>().AddRange(entitys); if (isSaveChange) { return await SaveChangesAsync() > 0; } return false; } #endregion #region 更新数据 public bool Update(T entity, bool isSaveChange = true, List<string> updatePropertyList = null) { if (entity==null) { return false; } _dbContext.Set<T>().Attach(entity); if (updatePropertyList==null) { _dbContext.Entry<T>(entity).State = EntityState.Modified;//全字段更新 } else { updatePropertyList.ForEach(c => { _dbContext.Entry(entity).Property(c).IsModified = true; //部分字段更新的写法 }); } if (isSaveChange) { return SaveChanges() > 0; } return false; } public bool Update(List<T> entitys, bool isSaveChange = true) { if (entitys==null||entitys.Count==0) { return false; } entitys.ForEach(c => { Update(c, false); }); if (isSaveChange) { return SaveChanges() > 0; } return false; } public async Task<bool> UpdateAsync(T entity, bool isSaveChange = true, List<string> updatePropertyList = null) { if (entity == null) { return false; } _dbContext.Set<T>().Attach(entity); if (updatePropertyList == null) { _dbContext.Entry<T>(entity).State = EntityState.Modified;//全字段更新 } else { updatePropertyList.ForEach(c => { _dbContext.Entry(entity).Property(c).IsModified = true; //部分字段更新的写法 }); } if (isSaveChange) { return await SaveChangesAsync() > 0; } return false; } public async Task<bool> UpdateAsync(List<T> entitys, bool isSaveChange = true) { if (entitys == null || entitys.Count == 0) { return false; } entitys.ForEach(c => { _dbContext.Set<T>().Attach(c); _dbContext.Entry<T>(c).State = EntityState.Modified; }); if (isSaveChange) { return await SaveChangesAsync() > 0; } return false; } #endregion #region 删除 public bool Delete(T entity, bool isSaveChange = true) { _dbContext.Set<T>().Attach(entity); _dbContext.Set<T>().Remove(entity); return isSaveChange ? SaveChanges() > 0 : false; } public bool Delete(List<T> entitys, bool isSaveChange = true) { entitys.ForEach(entity => { _dbContext.Set<T>().Attach(entity); _dbContext.Set<T>().Remove(entity); }); return isSaveChange ? SaveChanges() > 0 : false; } public virtual async Task<bool> DeleteAsync(T entity, bool isSaveChange = true) { _dbContext.Set<T>().Attach(entity); _dbContext.Set<T>().Remove(entity); return isSaveChange ? await SaveChangesAsync() > 0 : false; } public virtual async Task<bool> DeleteAsync(List<T> entitys, bool isSaveChange = true) { entitys.ForEach(entity => { _dbContext.Set<T>().Attach(entity); _dbContext.Set<T>().Remove(entity); }); return isSaveChange ? await SaveChangesAsync() > 0 : false; } #endregion public IQueryable<T> Entities => _dbContext.Set<T>().AsQueryable().AsNoTracking(); //public async Task<IQueryable<T>> EntitiesAsync => Task.Run(()=> _dbContext.Set<T>().AsQueryable().AsNoTracking()); public DatabaseFacade Database => _dbContext.Database; #region 查找 public T Get(object id) { return _dbContext.Set<T>().Find(id); } public T Get(Expression<Func<T, bool>> predicate = null) { return _dbContext.Set<T>().Where(predicate).AsNoTracking().FirstOrDefault(); } public async Task<T> GetAsync(object id) { return await _dbContext.Set<T>().FindAsync(id); } public async Task<T> GetAsync(Expression<Func<T, bool>> predicate = null) { return await _dbContext.Set<T>().Where(predicate).AsNoTracking().FirstOrDefaultAsync(); } public async Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate = null) { return await _dbContext.Set<T>().Where(predicate).AsNoTracking().ToListAsync(); } public async Task<IQueryable<T>> LoadAsync(Expression<Func<T, bool>> predicate = null) { if (predicate == null) { predicate = c => true; } return await Task.Run(() => _dbContext.Set<T>().Where(predicate).AsNoTracking()); } public void Dispose() { throw new NotImplementedException(); } #endregion }
3.表部分字段更新实现
EF默认的更新方式是一个实体对应的表全部字段更新,那么我们想更新表的部分字段怎么处理?
首先定义需要更新的字段:
public class PropertyExpression<T> where T : BaseEntity { private PropertyExpression() { } private static List<string> propertyList = new List<string>(); public static PropertyExpression<T> Init { get { propertyList.Clear(); return new PropertyExpression<T>(); } } public PropertyExpression<T> Property(Expression<Func<T, object>> expr) { var rtn = ""; if (expr.Body is UnaryExpression) { rtn = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name; } else if (expr.Body is MemberExpression) { rtn = ((MemberExpression)expr.Body).Member.Name; } else if (expr.Body is ParameterExpression) { rtn = ((ParameterExpression)expr.Body).Type.Name; } propertyList.Add(rtn); return this; } public List<string> ToList() { return propertyList; } }
EF更新的处理
public bool Update(T entity, bool isSaveChange = true, List<string> updatePropertyList = null) { if (entity==null) { return false; } _dbContext.Set<T>().Attach(entity); if (updatePropertyList==null) { _dbContext.Entry<T>(entity).State = EntityState.Modified;//全字段更新 } else { updatePropertyList.ForEach(c => { _dbContext.Entry(entity).Property(c).IsModified = true; //部分字段更新的写法 }); } if (isSaveChange) { return SaveChanges() > 0; } return false; }
使用
var entity = _unitOfWork.SysRoleRep.Get(model.RoleId); if (entity == null) { throw new Exception("要查找的对象不存在"); } entity.Name = model.RoleName; var updatedPropertyList = PropertyExpression<Sys_Role>.Init.Property(c => c.Name).ToList(); _unitOfWork.SysRoleRep.Update(entity, true, updatedPropertyList);
4.动态加载实体到DbContext
public class EntityTypeConfiguration<T> : IEntityTypeConfiguration<T> where T : class { public void Configure(EntityTypeBuilder<T> builder) { RelyConfigure(builder); } public virtual void RelyConfigure(EntityTypeBuilder<T> builder) { } }
public class Sys_Error_LogConfiguration : EntityTypeConfiguration<Sys_Error_Log> { public override void RelyConfigure(EntityTypeBuilder<Sys_Error_Log> builder) { builder.ToTable("sys_error_log"); builder.HasKey(x => x.Id); base.RelyConfigure(builder); } }
public class EfDbContext : DbContext { public EfDbContext(DbContextOptions<EfDbContext> options) : base(options) { } //配置数据库连接 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // optionsBuilder.UseSqlServer("xxxx connection string"); base.OnConfiguring(optionsBuilder); } //第一次使用EF功能时执行一次,以后不再执行 protected override void OnModelCreating(ModelBuilder modelBuilder) { //获取当前程序集中有基类并且基类是泛型的类 var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(c => c.BaseType != null && c.BaseType.IsGenericType).ToList(); foreach (var type in typesToRegister) { //泛型定义相同 if (type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)) { dynamic configurationInstance = Activator.CreateInstance(type); modelBuilder.ApplyConfiguration(configurationInstance); } } base.OnModelCreating(modelBuilder); } }
5.工作单元
工作单元是对仓储和事务的封装
原理参考:https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
public class EfUnitOfWork : IUnitOfWork { private EfDbContext _dbContext;//每次请求上下文只会创建一个 public EfUnitOfWork(EfDbContext context) { this._dbContext = context; } public int SaveChanges() { return _dbContext.SaveChanges(); } public async Task<int> SaveChangesAsync() { return await _dbContext.SaveChangesAsync(); } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { _dbContext.Dispose();//随着工作单元的销毁而销毁 } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public IDbContextTransaction BeginTransaction() { var scope = _dbContext.Database.BeginTransaction(); return scope; } public List<T> SqlQuery<T>(string sql, object param = null) where T : class { var con= _dbContext.Database.GetDbConnection(); if (con.State!= ConnectionState.Open) { con.Open(); } var list= MysqlDapperReader.SqlQuery<T>(con, sql, param); return list; //throw new NotImplementedException(); } public Task<List<T>> SqlQueryAsync<T>(string sql, object param = null) where T : class { throw new NotImplementedException(); } #region Sys Repository private IRepository<Sys_User> _sysUserRep; public IRepository<Sys_User> SysUserRep { get { if (_sysUserRep == null) { //var s= HttpContext.Current.Items["currentUser"]; //var s = HttpContext.Current.RequestServices.GetService<IRepository<Sys_User>>(); //HttpContext.RequestServices.GetService<IRepository<Sys_User>>(); _sysUserRep = new Repository<Sys_User>(_dbContext); } return _sysUserRep; } } private IRepository<Sys_Role> _sysRoleRep; public IRepository<Sys_Role> SysRoleRep { get { if (_sysRoleRep == null) { _sysRoleRep = new Repository<Sys_Role>(_dbContext); } return _sysRoleRep; } } private IRepository<Sys_Role_User> _sysRoleUserRep; public IRepository<Sys_Role_User> SysRoleUserRep { get { if (_sysRoleUserRep == null) { _sysRoleUserRep = new Repository<Sys_Role_User>(_dbContext); } return _sysRoleUserRep; } } private IRepository<Sys_Permission> _sysPermissionRep; public IRepository<Sys_Permission> SysPermissionRep { get { if (_sysPermissionRep == null) { _sysPermissionRep = new Repository<Sys_Permission>(_dbContext); } return _sysPermissionRep; } } private IRepository<Sys_Module> _sysModuleRep; public IRepository<Sys_Module> SysModuleRep { get { if (_sysModuleRep == null) { _sysModuleRep = new Repository<Sys_Module>(_dbContext); } return _sysModuleRep; } } private IRepository<Sys_Error_Log> _sysErrorLogRep; public IRepository<Sys_Error_Log> SysErrorLogRep { get { if (_sysErrorLogRep == null) { _sysErrorLogRep = new Repository<Sys_Error_Log>(_dbContext); } return _sysErrorLogRep; } } private IRepository<Sys_Operation_Log> _sysOperationLogRep; public IRepository<Sys_Operation_Log> SysOperationLogRep { get { if (_sysOperationLogRep == null) { _sysOperationLogRep = new Repository<Sys_Operation_Log>(_dbContext); } return _sysOperationLogRep; } } #endregion }
6.业务的实现方式
以前我是service中直接创建仓储然后用仓储操作数据库,方式如下:
这种方式比较繁琐,后来我将创建仓储统一放在工作单元中进行,在service中直接创建UnitOfWork,方式如下:
7.Service的动态注册
public static class AutoIocRegister { /// <summary> /// 动态注入IOC,注意类和接口的命名规则,接口在类名前面加"I" /// </summary> /// <param name="services"></param> /// <param name="assemblyName">程序集名称</param> public static void BatchAddScoped(this IServiceCollection services, string assemblyName) { var libs = DependencyContext.Default.CompileLibraries; var serviceLib = libs.Where(c => c.Name.Contains(assemblyName)).FirstOrDefault(); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(serviceLib.Name)); var serviceClassList = assembly.GetTypes().Where(c => c.IsClass).ToList(); foreach (var item in serviceClassList) { var interfaceName = "I" + item.Name; var interfaceType = assembly.GetTypes().Where(c => c.IsInterface && c.Name == interfaceName).FirstOrDefault(); if (interfaceType == null) continue; services.AddScoped(interfaceType, item); } } }
调用:
services.BatchAddScoped("Cloud.Service");
8.日志记录:
日志分操作日志和错误日志,可以设置在数据库和文本中同时记录:
通过全局过滤器GlobalExceptionFilter和GlobalAuthorizeFilter处理
9.前端的封装(分页、弹出层、ajax等)