极简实用的Asp.NetCore模块化框架新增CMS模块

简介: Asp.NetCore模块化框架新增CMS模块

简介

关于这个框架的背景,在前面我已经交代过了。不清楚的可以查看这个链接 极简实用的Asp.NetCore模块化框架决定免费开源了

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

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

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

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

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

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

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

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

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

AOP缓存

    {
        private static readonly ConcurrentDictionary<Type, MethodInfo> TypeofTaskResultMethod = new ConcurrentDictionary<Type, MethodInfo>();
        readonly int _expireSecond;
        readonly string _cacheKey;

        #region 拦截处理
        /// <summary>
        /// 过期时间,单位:分
        /// </summary>
        /// <param name="expireMin"></param>
        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<ICacheHelper>();
                if (!cache.Exists(key))
                {
                    return;
                }
                var strResult = cache.Get<string>(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}";
        }


        /// <summary>
        /// 获取被拦截方法返回值类型
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Type GetReturnType(AspectContext context)
        {
            return context.IsAsync()
                ? context.ServiceMethod.ReturnType.GetGenericArguments().First()
                : context.ServiceMethod.ReturnType;
        }

        /// <summary>
        /// 执行被拦截方法
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        private async Task<object> RunAndGetReturn(AspectContext context, AspectDelegate next)
        {
            await context.Invoke(next);
            return context.IsAsync()
            ? await context.UnwrapAsyncReturnValue()
            : context.ReturnValue;
        }

        /// <summary>
        /// 处理拦截器返回结果
        /// </summary>
        /// <param name="result"></param>
        /// <param name="returnType"></param>
        /// <param name="isAsync"></param>
        /// <returns></returns>
        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

多租户

    {
        /// <summary>
        /// 全局注册过滤器 ,自动为添加 更新方法赋值。也可自行手动打上特性标签
        /// </summary>
        /// <param name="context"></param>
        //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<Site>(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

    /// 适用于多租户模块使用
    /// </summary>
    /// <typeparam name="TEntity">实体</typeparam>
    /// <typeparam name="TDetailQuery">详情查询参数实体</typeparam>
    /// <typeparam name="TDeleteInput">删除实体</typeparam>
    /// <typeparam name="TListQuery">列表分页查询参数实体</typeparam>
    /// <typeparam name="TCreateInput">创建实体</typeparam>
    /// <typeparam name="TUpdateInput">更新实体</typeparam>
    [Route("api/[controller]/[action]")]
    [ApiController]
    [Authorize]
    [MultiTenant]
    public abstract class ApiTenantBaseController<TEntity, TDetailQuery, TDeleteInput, TListQuery, TCreateInput, TUpdateInput> : ControllerBase
       where TEntity : BaseSiteEntity, new()
       where TDetailQuery : DetailSiteQuery
       where TDeleteInput : DeletesSiteInput
       where TListQuery : ListSiteQuery
       where TCreateInput : class
       where TUpdateInput : class
    {
        private readonly IBaseServer<TEntity> _service;
        private readonly IMapper _mapper;

        public ApiTenantBaseController(IBaseServer<TEntity> service, IMapper mapper)
        {
            _service = service;
            _mapper = mapper;
        }
        /// <summary>
        /// 批量真实删除
        /// </summary>
        /// <param name="deleteInput"></param>
        /// <returns></returns>
        [HttpDelete]
        public virtual async Task<ApiResult> Deletes([FromBody] TDeleteInput deleteInput)
        {
            var res = await _service.DeleteAsync(deleteInput.Ids);
            if (res <= 0)
            {
                throw new FriendlyException("删除失败了!");
            }
            return new ApiResult();
        }
        /// <summary>
        /// 单个真实删除
        /// </summary>
        /// <param name="deleteInput"></param>
        /// <returns></returns>
        [HttpDelete]
        public virtual async Task<ApiResult> 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();
        }
        /// <summary>
        /// 软删除
        /// </summary>
        /// <param name="deleteInput"></param>
        /// <returns></returns>
        [HttpDelete]
        public virtual async Task<ApiResult> 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();
        }
        /// <summary>
        /// 列表分页
        /// </summary>
        /// <param name="listQuery">参数实体</param>
        /// <returns></returns>
        [HttpGet]
        public virtual async Task<ApiResult> 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 });
        }

        /// <summary>
        /// 详情
        /// </summary>
        /// <param name="detailQuery">参数实体</param>
        /// <returns></returns>
        [HttpGet]
        public virtual async Task<ApiResult> 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);
        }
        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="createInput">添加实体</param>
        /// <returns></returns>
        [HttpPost]
        public virtual async Task<ApiResult> Add([FromBody] TCreateInput createInput)
        {
            var entity = _mapper.Map<TEntity>(createInput);
            var res = await _service.AddAsync(entity);
            if (res <= 0)
            {
                throw new FriendlyException("添加失败了!");
            }
            return new ApiResult(data: res);
        }
        /// <summary>
        /// 修改-默认忽略更新CreateTime字段
        /// </summary>
        /// <param name="updateInput">修改实体</param>
        /// <returns></returns>
        [HttpPut]
        public virtual async Task<ApiResult> Modify([FromBody] TUpdateInput updateInput)
        {
            var entity = _mapper.Map<TEntity>(updateInput);
            var res = await _service.UpdateAsync(entity, d => new { d.CreateTime });
            if (res <= 0)
            {
                throw new FriendlyException("修改失败了!");
            }
            return new ApiResult(data: res);
        }
    }

效果图

1046844-20210406165024767-816936429.png
1046844-20210406165133234-1783888589.png
1046844-20210406165256857-847883567.png

总结

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

源码地址
码云:https://gitee.com/shenniu_code_group/shen-nius.-modularity

github:https://github.com/realyrare/ShenNiusFramework

相关文章
|
开发框架 JSON 前端开发
极简实用的Asp.NetCore模块化框架决定免费开源了
在开发这个框架之前,前前后后看过好几款模块化的框架,最后在一段时间内对ABP VNext痛下狠心,研究一段时间后,不得不说 ABP VNext的代码层面很规范,也都是一些最佳实践,开发出一个模块效率也很高。但不得不说,于我而言太重了。还看过一些其他模块化的框架,就不说了,学习成本于我而言太高了,主要是自己懒。
189 0
极简实用的Asp.NetCore模块化框架决定免费开源了
|
前端开发 .NET 开发框架
转发-基于ASP.NET MVC 4/5 Razor的模块化/插件式架构实现
基于ASP.NET MVC 4/5 Razor的模块化/插件式架构实现   概述   在日常开发中, 我们经常谈起模块化/插件化架构,这样可既可以提高开效率,又可以实现良好的扩展性,尤其对于产品化的系统有更好的实用性。
970 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0
|
17天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0
|
2月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
112 5
|
9月前
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
124 0
|
10月前
|
开发框架 前端开发 .NET
[回馈]ASP.NET Core MVC开发实战之商城系统(一)
[回馈]ASP.NET Core MVC开发实战之商城系统(一)
119 0
|
10月前
|
SQL 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(开篇)
[回馈]ASP.NET Core MVC开发实战之商城系统(开篇)
145 0