ASP.NET CORE写一个缓存Attribute工具

前言:

本工具启发于Spring Cache,故使用C#的Attribute去模仿Spring的@Cahce相关的注解,试图实现一样的效果。
代码地址:https://gitee.com/godenSpirit/cache-attribute,
欢迎提出意见和改善。

结构:

请添加图片描述

Attribute注解

using System;
using System.Collections.Generic;
using System.Text;

namespace CacheAttribute.Attributes
{

    [AttributeUsage(validOn: AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public class CacheAbleAttribute : Attribute
    {
    	//缓存名前缀
        public string preffix { get; }
		//缓存名主体-----(可以解析方法参数的值)
        public string Totoal_Name { get; }
		//是否生效
        public bool work {get;}
	    //缓存时间
        public int Expire;

        public CacheAbleAttribute(string preffix, bool work, string totoal_Name, int expire = 1800)
        {
            this.preffix = preffix;
            Totoal_Name = totoal_Name;
            this.work = work;
            Expire = expire;
        }
    }
}

Cache接口和实现

using System;
using System.Collections.Generic;
using System.Text;

namespace CacheAttribute.Utils
{
    public interface ICacheable
    {
        /// <summary>
        /// 泛型获取
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        T Get<T>(string key);

        /// <summary>
        /// object获取
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        object Get(string key);


        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="Expire"></param>
        /// <returns></returns>
        bool Set(string key, object obj, int Expire = 1800);

        /// <summary>
        /// 缓存是否存在
        /// </summary>
        /// <param name="Key"></param>
        /// <returns></returns>
        bool Exist(string Key);

        /// <summary>
        /// 移除缓存
        /// </summary>
        /// <param name="Key"></param>
        /// <returns></returns>
        bool Remove(string Key);

        /// <summary>
        /// 匹配移除
        /// </summary>
        /// <param name="Pattern"></param>
        /// <returns></returns>
        bool RemovePattern(string Pattern);
    }
}

Redis的实现类

using SugarRedis;
using System;
using System.Collections.Generic;
using System.Text;

namespace CacheAttribute.Utils
{
    public class RedisCahceUtil : ICacheable
    {
		//这里直接使用了SqlSugar提供的Redis工具
        private static SugarRedisClient client;

        private static RedisCahceUtil instance;
		//使用单例模式
        private RedisCahceUtil(String config = null) {
            if (!string.IsNullOrEmpty(config))
                client = new SugarRedisClient(config);
            else
                client = new SugarRedisClient();
        }
	    //如果不是本地Redis的测试环境,请从Configuration中传入连接参数
        public static RedisCahceUtil GetInstance()
        {
            if (instance == null)
            {
                instance = new RedisCahceUtil();
                return instance;
            }
            else
                return instance;
        }

        public bool Exist(string Key)
        {
            return client.Exists(Key);
        }

        public T Get<T>(string key)
        {
            return client.Get<T>(key);
        }

        public object Get(string key)
        {
            return client.Get(key);
        }

        public bool Remove(string Key)
        {
            try
            {
                client.Remove(Key);
                return true;
            }
            catch(Exception e)
            {
                return false;
            }
            
        }

        public bool RemovePattern(string Pattern)
        {
            try
            {
                client.RemoveCacheRegex(Pattern);
                return true;
            }catch(Exception e)
            {
                return false;
            }
        }

        public bool Set(string key, object obj, int Expire = 1800)
        {
            return client.Set(key, obj, Expire);
        }
    }
}

CacheFilter

using CacheAttribute.Attributes;
using CacheAttribute.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CacheAttribute.Filter
{
    /// <summary>
    /// 异步过滤器,调用缓存方法
    /// </summary>
    public class AsyncCacheAtttibuteCheckFilter : IAsyncActionFilter
    {
        //缓存工具
        private readonly ICacheable _cache;

        //缓存策略
        private readonly CacheOperation operation;

        //日志工具
        private readonly ILogger<AsyncCacheAtttibuteCheckFilter> logger;

        public AsyncCacheAtttibuteCheckFilter(ICacheable cache, CacheOperation operation, ILogger<AsyncCacheAtttibuteCheckFilter> logger)
        {
            _cache = cache;
            this.operation = operation;
            this.logger = logger;
        }


        /// <summary>
        /// 异步过滤方法,可以用Invoke来使原方法执行
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            #region 方法执行前
            logger.LogInformation("方法执行前,获取参数");

            //强转获取cntroller实例
            ControllerActionDescriptor controller = (ControllerActionDescriptor)context.ActionDescriptor;
            //获取调用接口的反射对象
            MethodInfo action = controller.MethodInfo;

            //获取Cacheable注解
            CacheAbleAttribute cacheable = (CacheAbleAttribute)action.GetCustomAttribute(typeof(CacheAbleAttribute));

            //获取CacheDel注解
            CacheDelAttribute cacheDel = (CacheDelAttribute)action.GetCustomAttribute(typeof(CacheDelAttribute));

            //获取CacheUpdate注解
            CacheUpdateAttribute cacheUpdate = (CacheUpdateAttribute)action.GetCustomAttribute(typeof(CacheUpdateAttribute));

            #region CacheAble注解处理
            //当有cacheable注解时
            if (cacheable != null)
            {
                //当使用CacheAble时,同时也具有CacheInit时,先处理CacheInit
                if (action.GetCustomAttribute(typeof(CacheInitAttribute)) != null)
                {
                    bool isSus = operation.CacheInit_strategy(action, _cache);
                }

                operation.CacheAble_Strategy_Before(context, _cache);

               

            }
            #endregion

            //删除正则匹配到的缓存
            if (cacheDel != null)
            {
                if (!operation.CacheDel_strategy(action, _cache))
                {
                    logger.LogInformation(this.GetType().Name + ":" + "没有相应的缓存被删除");
                }
                else
                {
                    logger.LogInformation(this.GetType().Name + ":" + "删除了相应的缓存");
                }
            }

            //相当于强化版的Cacheable注解
            if (cacheUpdate != null && cacheUpdate.work == true)
            {
                operation.CacheUpdate_strategy_Before(context, _cache);

            }

            #endregion 

            //await next();

            #region 方法执行后
            logger.LogInformation("方法执行后,去获取结果");

            Task<ActionExecutedContext> task = next.Invoke();
            
            ControllerActionDescriptor controller2 = (ControllerActionDescriptor)context.ActionDescriptor;

            MethodInfo action2 = controller.MethodInfo;

            //获取cacheable注解
            CacheAbleAttribute cacheable2 = (CacheAbleAttribute)action2.GetCustomAttribute(typeof(CacheAbleAttribute));

            //获取cacheUpdate注解
            CacheUpdateAttribute cacheUpdate2 = (CacheUpdateAttribute)action2.GetCustomAttribute(typeof(CacheUpdateAttribute));

            //当有cacheable注解时
            if (cacheable2 != null && cacheable2.work == true)
            {

                operation.CacheAble_Strategy_After(task.Result,context, _cache);

            }

            if (cacheUpdate2 != null && cacheUpdate2.work == true)
            {


                operation.CacheUpdate_strategy_After(task.Result,context, _cache);

            }
            #endregion

        }
    }
}

将执行代码通过策略模式抽取到CacheOperation

/// <summary>
    /// 缓存选项
    /// 主要是提供自定义缓存策略的方法
    /// </summary>
    public class CacheOperation
    {
        private readonly ILogger<CacheOperation> logger;
        /// <summary>
        /// 构造函数
        /// </summary>
        public CacheOperation(ILogger<CacheOperation> logger)
        {
            this.logger = logger;
            //为了简洁,我将提供的默认策略初始化省略,当然大家也可以在注入时自己设置自定义的策略
			........
           
        }

        /// <summary>
        /// 自定义缓存策略------------方法执行前
        /// </summary>
        public Action<ActionExecutingContext,ICacheable> CacheAble_Strategy_Before { get; set; }

        /// <summary>
        /// 自定义缓存策略-------------方法执行后
        /// </summary>
        public Action<ActionExecutedContext,ActionExecutingContext,ICacheable> CacheAble_Strategy_After { get; set; }

        /// <summary>
        /// 自定义删除策略
        /// </summary>
        public Func<ActionExecutingContext, ICacheable, bool> CacheDel_strategy { get; set; }

        /// <summary>
        /// 自定义更新策略-------------方法执行前
        /// </summary>
        public Action<ActionExecutingContext, ICacheable> CacheUpdate_strategy_Before { get; set; }

        /// <summary>
        /// 自定义更新策略-------------方法执行后
        /// </summary>
        public Action<ActionExecutedContext,ActionExecutingContext, ICacheable> CacheUpdate_strategy_After { get; set; }

        /// <summary>
        /// 自定义初始化缓存值
        /// </summary>
        public Func<MethodInfo, ICacheable,bool> CacheInit_strategy { get; set; }

    }
}

缓存名解析

using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;

namespace CacheAttribute.Filter
{
    /// <summary>
    /// 缓存名称过滤器
    /// 主要从来对注解中设置的缓存名Total_Name做解析,使之能够像@CacheAble一样解析方法参数
    /// eg:#arg,#arg.pro
    /// </summary>
    public class CacheNameResolver
    {
        /// <summary>
        /// 解析正确的TotalName
        /// </summary>
        /// <param name="TotalName"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        
        public static string ResolveTotalName([Required]string TotalName,ActionExecutingContext context)
        {
            //目标缓存名
            string TargetCacheName = TotalName;

            

            //获取方法的所有参数信息(这个方法可以获取参数的属性&名称信息,无法拿到参数的值
            List<ParameterDescriptor> parameters_list = context.ActionDescriptor.Parameters.ToList();

            //这个方法可以拿到值,但不能获取参数的属性信息
            IDictionary<string, object> argsMap = context.ActionArguments;

            //item==切面方法参数
            parameters_list.ForEach(item => {

                //如果参数为基础类型,则直接替换为参数值
                if (TargetCacheName.Contains("#" + item.Name) && IsBasicType(item.ParameterType))
                {
                    TargetCacheName =  TargetCacheName.Replace("#" + item.Name, argsMap[item.Name].ToString());
                }

                //接下来考虑参数为引用类型的情况下,再去获取一次下属属性

                PropertyInfo[] propertyInfos = item.ParameterType.GetProperties();

                foreach(PropertyInfo pro in propertyInfos)
                {
                    //如果是引用类型则通过.可以获取下属的属性值
                    if (TargetCacheName.Contains("#" + item.Name + "." + pro.Name)&&IsBasicType(pro.PropertyType))
                    {
                        TargetCacheName = TargetCacheName.Replace("#" + item.Name + "." + pro.Name, pro.GetValue(argsMap[item.Name]).ToString());
                    }
                }

                
            });

            return TargetCacheName;
        }


       

       



        //工具方法-------判断一个类型是否是基础数据类型
        public static bool IsBasicType(Type type)
        {
            if (type.Equals(typeof(int)) ||
                type.Equals(typeof(double)) ||
                type.Equals(typeof(float)) ||
                type.Equals(typeof(bool)) ||
                type.Equals(typeof(string)) ||
                type.Equals(typeof(byte)) ||
                type.Equals(typeof(char)) ||
                type.Equals(typeof(long)) ||
                type.Equals(typeof(DateTime)) ||
                type.Equals(typeof(decimal))
                )
            {
                return true;
            }
            return false;
        }

    }
}

使用方法

使用方法非常简单

//注册缓存实现实例
builder.Services.AddSingleton(typeof(ICacheable), RedisCahceUtil.GetInstance());
//注册缓存策略类-----(可以先创建,然后再自定义相关策略再注册到容器)
builder.Services.AddSingleton(typeof(CacheOperation));
//添加该Filer即可
builder.Services.AddMvc(option => {
    option.Filters.Add(typeof(AsyncCacheAtttibuteCheckFilter));
});

测试

在Gitee地址中自带一个WebAPI的测试,可以直接在其基础上扩写

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马苏丹默罕默德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值