基于.net core 2.0+mysql+AceAdmin搭建一套快速开发框架
前言
.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等)
先放几张图吧,详细的以后再介绍
演示地址:http://cloud.eggtwo.com/main/index
基于.net core 2.0+mysql+AceAdmin搭建一套快速开发框架的更多相关文章
- 基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【六】【引入bootstrap前端框架】
https://blog.csdn.net/linzhefeng89/article/details/78752658 基于springboot+bootstrap+mysql+redis搭建一套完整 ...
- 一个基于 .NET Core 2.0 开发的简单易用的快速开发框架 - LinFx
LinFx 一个基于 .NET Core 2.0 开发的简单易用的快速开发框架,遵循领域驱动设计(DDD)规范约束,提供实现事件驱动.事件回溯.响应式等特性的基础设施.让开发者享受到正真意义的面向对象 ...
- 基于.Net Core 5.0 Worker Service 的 Quart 服务
前言 看过我之前博客的人应该都知道,我负责了相当久的部门数据同步相关的工作.其中的艰辛不赘述了. 随着需求的越来越复杂,最近windows的计划任务已经越发的不能满足我了,而且计划任务毕竟太弱智,总是 ...
- .Net Core 2.0 的 ConsoleApp 搭建 Quartz(xml配置)windows服务
零.创建一个.Net Core 2.0 的ConsoleApp 应用,建完就是这个样子了. 添加Log4Net 的引用,(不想看可以不看,个人习惯)Install-Package log4net添加C ...
- win10下ASP.NET Core 2.0部署环境搭建(转)
此文用于记录在win10环境下,新建的Asp.net Core 2.0 Web应用项目如何运行在IIS上 一.运行环境 操作系统: Window10 家庭中文版 版本 10.0.15063 版本 15 ...
- SpringCloud微服务实战——搭建企业级开发框架(四十六):【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建
近年来uni-app发展势头迅猛,只要会vue.js,就可以开发一套代码,发布移动应用到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/ ...
- 基于.NetCore的Redis5.0.3(最新版)快速入门、源码解析、集群搭建与SDK使用【原创】
1.[基础]redis能带给我们什么福利 Redis(Remote Dictionary Server)官网:https://redis.io/ Redis命令:https://redis.io/co ...
- Skywalking入门介绍,skywalking6.5.0 +mysql (windows) 搭建
一. 介绍 1. 基本信息 SkyWalking 创建于2015年,提供分布式追踪功能.从5.x开始,项目进化为一个完成功能的Application Performance Monitoring系统. ...
- 基于.Net Core的API框架的搭建(1)
目标 我们的目标是要搭建一个API控制器的项目,API控制器提供业务服务. 一.开发框架搭建 1.开发前准备 开发前,我们需要下载如下软件,安装过程略: (1) 开发工具:VS2017 (2) 数据库 ...
随机推荐
- SQL0419N 十进制除法运算无效,因为结果将有一个负小数位。 SQLSTATE=42911
select case when sum(qty_sold*u.um03/u.um08) <> 0 then decimal(coalesce(sum(d.amt_sold_with_ta ...
- Python3创建项目时创建了一个叫做“keyword"的包,运行项目时报ImportError: cannot import name 'iskeyword'错误
导致该问题的原因为在Python3中keyword是python的关键字包,所以在给包命名时应避免使用关键字进行命名.解决方法,将keword包名称修改为'keywords'就可以了.
- Mysql常用命令 详细整理版
Mysql常用命令 show databases; 显示数据库 create database name; 创建数据库 use databasename; 选择数据库 drop database na ...
- 【机器学习】异常检测算法(I)
在给定的数据集,我们假设数据是正常的 ,现在需要知道新给的数据Xtest中不属于该组数据的几率p(X). 异常检测主要用来识别欺骗,例如通过之前的数据来识别新一次的数据是否存在异常,比如根据一个用户以 ...
- eclipse中修改maven本地仓库方式
1.安装maven,教程可参考:https://jingyan.baidu.com/article/4f7d5712a1306c1a21192746.html 2.安装成功后,可以打开eclipse新 ...
- spring深入学习(二)-----bean的生命周期、IOC容器bean装配
bean的生命周期 1.实例化Bean对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBea ...
- 关于Bell数的一道题目
考虑 T3+1 {1,2,3,4} T3是3个元素的划分,如果在里面加入子集{4}, 4被标成特殊元素, 就形成了T4一类的划分(里面的子集的并集是{1,2,3,4}) T2是2个元素的划 ...
- Unity自动切割动画
最近在开发项目时,需要处理大量的动画,于是就网上查找资料,然后写了这么编辑器工具: 就是在模型导入时,根据配置文件自动切割动画. 首先我们需要封装两个类:一个模型类和一个动画类 public clas ...
- 公用表表达式 (CTE)、递归、所有子节点、sqlserver
指定临时命名的结果集,这些结果集称为公用表表达式 (CTE).公用表表达式可以包括对自身的引用.这种表达式称为递归公用表表达式. 对于递归公用表达式来说,实现原理也是相同的,同样需要在语句中定义两部分 ...
- 下划线“_”在oracle中不是单纯的表示下划线的意思,而是表示匹配单一任何字符!
[解决办法]1.使用 escape() 函数escape关键字经常用于使某些特殊字符,如通配符:'%','_'转义为它们原来的字符的意义,被定义的转义字符通常使用'\',但是也可以使用其他的符号.例如 ...