前言

.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搭建一套快速开发框架的更多相关文章

  1. 基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【六】【引入bootstrap前端框架】

    https://blog.csdn.net/linzhefeng89/article/details/78752658 基于springboot+bootstrap+mysql+redis搭建一套完整 ...

  2. 一个基于 .NET Core 2.0 开发的简单易用的快速开发框架 - LinFx

    LinFx 一个基于 .NET Core 2.0 开发的简单易用的快速开发框架,遵循领域驱动设计(DDD)规范约束,提供实现事件驱动.事件回溯.响应式等特性的基础设施.让开发者享受到正真意义的面向对象 ...

  3. 基于.Net Core 5.0 Worker Service 的 Quart 服务

    前言 看过我之前博客的人应该都知道,我负责了相当久的部门数据同步相关的工作.其中的艰辛不赘述了. 随着需求的越来越复杂,最近windows的计划任务已经越发的不能满足我了,而且计划任务毕竟太弱智,总是 ...

  4. .Net Core 2.0 的 ConsoleApp 搭建 Quartz(xml配置)windows服务

    零.创建一个.Net Core 2.0 的ConsoleApp 应用,建完就是这个样子了. 添加Log4Net 的引用,(不想看可以不看,个人习惯)Install-Package log4net添加C ...

  5. win10下ASP.NET Core 2.0部署环境搭建(转)

    此文用于记录在win10环境下,新建的Asp.net Core 2.0 Web应用项目如何运行在IIS上 一.运行环境 操作系统: Window10 家庭中文版 版本 10.0.15063 版本 15 ...

  6. SpringCloud微服务实战——搭建企业级开发框架(四十六):【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建

      近年来uni-app发展势头迅猛,只要会vue.js,就可以开发一套代码,发布移动应用到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/ ...

  7. 基于.NetCore的Redis5.0.3(最新版)快速入门、源码解析、集群搭建与SDK使用【原创】

    1.[基础]redis能带给我们什么福利 Redis(Remote Dictionary Server)官网:https://redis.io/ Redis命令:https://redis.io/co ...

  8. Skywalking入门介绍,skywalking6.5.0 +mysql (windows) 搭建

    一. 介绍 1. 基本信息 SkyWalking 创建于2015年,提供分布式追踪功能.从5.x开始,项目进化为一个完成功能的Application Performance Monitoring系统. ...

  9. 基于.Net Core的API框架的搭建(1)

    目标 我们的目标是要搭建一个API控制器的项目,API控制器提供业务服务. 一.开发框架搭建 1.开发前准备 开发前,我们需要下载如下软件,安装过程略: (1) 开发工具:VS2017 (2) 数据库 ...

随机推荐

  1. Gym - 101848B Almost AP 暴力

    题目链接:http://codeforces.com/gym/101848/problem/B 给出一串数字要你最多改动三个数字使这一串数字成为等差数列.因为最多改动三个数字所以可以先求出相邻两项的差 ...

  2. Finance财务软件(自定义报表专题)

    我们可以通过存储过程自定义报表 1.在菜单中新增报表菜单,这里的代码约束为报表对应存储过程名称,配置完成成后重启客户端生效 2.在自定义模板中适配存储过程入参,这里的功能键值为存储过程名称,字段键值与 ...

  3. 数据库镜像转移Failover Partner

    数据库主体镜像转换:任务 - 镜像 - 故障转移 sqlserver2008 数据库镜像服务配置完成后,大家会发现我们有了两个数据库服务,这两个服务可以实现自动故障转移,那么我们的程序如何实现自动连接 ...

  4. vue-cli 第二章 (常见问题修正)

    一.编译打包多出 *.map 文件处理   当执行 npm run build 后根目录下会编译出一个 dist 的文件夹,如下:     其中 css 和 js  文件夹下会多出一些 *.map 的 ...

  5. XBee PRO 900HP远距离无线模块

    XBee PRO S3B也称为XBee-900HP无线模块,它是一款工作在频段900~928MHz之间,基于FHSS跳频技术的远距离无线数传电台核心模块.常用型号如下: 类别 型号 开发套件 XKB9 ...

  6. opencv的安装

    网上搜了好多文章安装opencv3.2.0都未能成功,写的也个不相同,后来找到了opencv官网的教程,看了后才发现,这上面才是最详细的. 于是按照opencv官网教程安装,安装的一半就中断了.经过苦 ...

  7. 父组件传值给子组件的v-model属性

    父组件如何修改子组件中绑定的v-model属性 因为v-model属性是双向数据绑定,而vue的通信方式又是单向通信,所以,当子组件想要改变父组件传过来的值的属性时,就会报错,典型的就是父组件传值给子 ...

  8. Eclipse的设置

    1 Eclipse的工作空间和新建工程 1.1: 工作空间 * 其实就是我们写的源代码所在的目录 1.2: 创建工程(项目) * 右键/Package Explore 空白区/new /Java Pr ...

  9. Redis的启动及配置

    在redis已经安装完成的情况下,进入redis/bin目录下,输入命令: ./redis-server,就可以直接启动redis了,效果如图所示: 但是此时终端无法进行任何操作,按CTRL+c命令, ...

  10. 大数据项目测试<二>项目的测试工作

    大数据的测试工作: 1.模块的单独测试 2.模块间的联调测试 3.系统的性能测试:内存泄露.磁盘占用.计算效率 4.数据验证(核心) 下面对各个模块的测试工作进行单独讲解. 0. 功能测试 1. 性能 ...