博客
关于我
极简实用的Asp.NetCore模块化框架新增CMS模块
阅读量:435 次
发布时间:2019-03-06

本文共 9578 字,大约阅读时间需要 31 分钟。

简介

关于这个框架的背景,在前面我已经交代过了。不清楚的可以查看这个链接 

在最近一段时间内,对这个框架新增了以下功能:

1、新增了CMS模块,目前整体都比较简单,适合个人博客使用。

2、新增了AOP缓存,使用AspectCore,缓存可做到Memarycache和redis一件切换。

3、新增AOP事务,服务层和控制器都可以打上特性标签使用。

4、对多租户使用Filter,不管是添加还是更新、查询即可自动赋值。

5、新增七牛云图片上传功能。

6、对于单表的增删改查,在控制器内做了封装,有新的业务按约定建立对应的CRUD实体,一套API自动完成。

7、后台管理新增站群管理。

说了那么多,让我们上点代码和截图来瞧一瞧吧。

AOP缓存

public class CacheInterceptorAttribute : AbstractInterceptorAttribute    {        private static readonly ConcurrentDictionary
TypeofTaskResultMethod = new ConcurrentDictionary
(); readonly int _expireSecond; readonly string _cacheKey; #region 拦截处理 ///
/// 过期时间,单位:分 /// ///
public CacheInterceptorAttribute(string cacheKey = null, int expireMin = -1) { _expireSecond = expireMin * 60; _cacheKey = cacheKey; } public async override Task Invoke(AspectContext context, AspectDelegate next) { try { string key = string.Empty; //自定义的缓存key不存在,再获取类名+方法名或类名+方法名+参数名的组合式key if (!string.IsNullOrEmpty(_cacheKey)) { key = _cacheKey; } else { key = GetKey(context.ServiceMethod, context.Parameters); } var returnType = GetReturnType(context); var cache = context.ServiceProvider.GetService
(); if (!cache.Exists(key)) { return; } var strResult = cache.Get
(key); var result = JsonConvert.DeserializeObject(strResult, returnType); if (result != null) { context.ReturnValue = ResultFactory(result, returnType, context.IsAsync()); } else { result = await RunAndGetReturn(context, next); if (_expireSecond > 0) { cache.Set(key, result, TimeSpan.FromMinutes(_expireSecond)); } else { cache.Set(key, result); } } } catch (Exception e) { Console.WriteLine(e.Message); } } private static string GetKey(MethodInfo method, object[] parameters) { return GetKey(method.DeclaringType.Name, method.Name, parameters); } private static string GetKey(string className, string methodName, object[] parameters) { var paramConcat = parameters.Length == 0 ? string.Empty : ":" + JsonConvert.SerializeObject(parameters); return $"{className}:{methodName}{paramConcat}"; } ///
/// 获取被拦截方法返回值类型 /// ///
///
private Type GetReturnType(AspectContext context) { return context.IsAsync() ? context.ServiceMethod.ReturnType.GetGenericArguments().First() : context.ServiceMethod.ReturnType; } ///
/// 执行被拦截方法 /// ///
///
///
private async Task
RunAndGetReturn(AspectContext context, AspectDelegate next) { await context.Invoke(next); return context.IsAsync() ? await context.UnwrapAsyncReturnValue() : context.ReturnValue; } /// /// 处理拦截器返回结果 /// /// /// /// ///
private object ResultFactory(object result, Type returnType, bool isAsync) { return !isAsync ? result : TypeofTaskResultMethod .GetOrAdd(returnType, t => typeof(Task) .GetMethods() .First(p => p.Name == "FromResult" && p.ContainsGenericParameters) .MakeGenericMethod(returnType)) .Invoke(null, new object[] { result }); } #endregion

多租户

public class MultiTenantAttribute : ActionFilterAttribute, IActionFilter    {        ///         /// 全局注册过滤器 ,自动为添加 更新方法赋值。也可自行手动打上特性标签        ///         ///         //private string[] methods = new string[] { "add", "modify" };        public override void OnActionExecuting(ActionExecutingContext context)        {            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;            var actionName = actionDescriptor.ActionName.ToLower();            ICacheHelper cache = context.HttpContext.RequestServices.GetRequiredService(typeof(ICacheHelper)) as ICacheHelper;            var siteId = cache.Get
(KeyHelper.Cms.CurrentSite)?.Id; //如果是增加和修改方法 根据站群id //if (methods.Any(o => actionName.Contains(o))) //{ foreach (var parameter in actionDescriptor.Parameters) { var parameterName = parameter.Name;//获取Action方法中参数的名字 var parameterType = parameter.ParameterType;//获取Action方法中参数的类型 //if (!typeof(int).IsAssignableFrom(parameterType))//如果不是ID类型 //{ // continue; //} //自动添加租户id if (typeof(IGlobalSite).IsAssignableFrom(parameterType)) { var model = context.ActionArguments[parameterName] as IGlobalSite; if (siteId != null) { model.SiteId = siteId.Value; } } } //} } }}

控制器单表CRUD API

///     /// 适用于多租户模块使用    ///     /// 
实体
///
详情查询参数实体
///
删除实体
///
列表分页查询参数实体
///
创建实体
///
更新实体
[Route("api/[controller]/[action]")] [ApiController] [Authorize] [MultiTenant] public abstract class ApiTenantBaseController
: ControllerBase where TEntity : BaseSiteEntity, new() where TDetailQuery : DetailSiteQuery where TDeleteInput : DeletesSiteInput where TListQuery : ListSiteQuery where TCreateInput : class where TUpdateInput : class { private readonly IBaseServer
_service; private readonly IMapper _mapper; public ApiTenantBaseController(IBaseServer
service, IMapper mapper) { _service = service; _mapper = mapper; } ///
/// 批量真实删除 /// ///
///
[HttpDelete] public virtual async Task
Deletes([FromBody] TDeleteInput deleteInput) { var res = await _service.DeleteAsync(deleteInput.Ids); if (res <= 0) { throw new FriendlyException("删除失败了!"); } return new ApiResult(); } ///
/// 单个真实删除 /// ///
///
[HttpDelete] public virtual async Task
Delete([FromBody] TDeleteInput deleteInput) { foreach (var item in deleteInput.Ids) { var res = await _service.DeleteAsync(d => d.Id == item && d.SiteId == deleteInput.SiteId); if (res <= 0) { throw new FriendlyException("删除失败了!"); } } return new ApiResult(); } ///
/// 软删除 /// ///
///
[HttpDelete] public virtual async Task
SoftDelete([FromBody] TDeleteInput deleteInput) { foreach (var item in deleteInput.Ids) { var res = await _service.UpdateAsync(d => new TEntity() { Status = false }, d => d.Id == item && d.SiteId == deleteInput.SiteId&&d.Status==true); if (res <= 0) { throw new FriendlyException("删除失败了!"); } } return new ApiResult(); } ///
/// 列表分页 /// ///
参数实体 ///
[HttpGet] public virtual async Task
GetListPages([FromQuery] TListQuery listQuery) { var res = await _service.GetPagesAsync(listQuery.Page, listQuery.Limit, d => d.SiteId == listQuery.SiteId&&d.Status==true, d => d.Id, false); return new ApiResult(data: new { count = res.TotalItems, items = res.Items }); } ///
/// 详情 /// ///
参数实体 ///
[HttpGet] public virtual async Task
Detail([FromQuery] TDetailQuery detailQuery) { var res = await _service.GetModelAsync(d => d.Id == detailQuery.Id && d.SiteId == detailQuery.SiteId&&d.Status==true); return new ApiResult(data: res); } ///
/// 添加 /// ///
添加实体 ///
[HttpPost] public virtual async Task
Add([FromBody] TCreateInput createInput) { var entity = _mapper.Map
(createInput); var res = await _service.AddAsync(entity); if (res <= 0) { throw new FriendlyException("添加失败了!"); } return new ApiResult(data: res); } ///
/// 修改-默认忽略更新CreateTime字段 /// ///
修改实体 ///
[HttpPut] public virtual async Task
Modify([FromBody] TUpdateInput updateInput) { var entity = _mapper.Map
(updateInput); var res = await _service.UpdateAsync(entity, d => new { d.CreateTime }); if (res <= 0) { throw new FriendlyException("修改失败了!"); } return new ApiResult(data: res); } }

 效果图

总结

好了,又要到说再见的时候了,框架我只要有时间就会一直更新下去,不合理的地方欢迎浏览代码指导批评,我希望这个框架从简单的一点一滴做起,慢慢地把它做大做强。算是程序员阶段最后一次做框架了,什么时候不更新了,有可能就转行了。大家也可以不使用这个框架,只要里面地思路能帮助到一部分人,我认为这就足够了。

 

源码地址

码云:

 

github:

喜欢交流的人进微信群

 

转载地址:http://hoqyz.baihongyu.com/

你可能感兴趣的文章
100天搞定机器学习|Day9-12 支持向量机
查看>>
100天搞定机器学习|Day19-20 加州理工学院公开课:机器学习与数据挖掘
查看>>
100天搞定机器学习|Day22 机器为什么能学习?
查看>>
100天搞定机器学习|day37 无公式理解反向传播算法之精髓
查看>>
数据工程师必备的8项技能,不要只知道Python!
查看>>
R in action读书笔记(3)-第六章:基本图形
查看>>
R in action读书笔记(19)第十四章 主成分和因子分析
查看>>
iOS UIAlertController
查看>>
iOS UISlider的使用
查看>>
iOS Xcode 打包之后,不能输出日志
查看>>
UIPickerView的使用(二)
查看>>
iOS 多线程GCD简介
查看>>
实现延迟消息队列
查看>>
写了一下 micropython 的文件系统单元测试
查看>>
说说字库和字模的故事,然后在 MaixPy 里实现打印中文字体(任意字体)吧!
查看>>
linux kernel version magic 不一致导致的模块 加载 (insmod) 不上
查看>>
线性代数应该这样学9:上三角矩阵、对角矩阵
查看>>
【科学计算】插值理论
查看>>
centos7一步一步搭建docker jenkins 及自定义访问路径重点讲解
查看>>
在wxPython使ListCtrl占据整个窗口
查看>>