.NET + AOP + AutoFac + Redis

直接上代码

using Autofac;
using Autofac.Extras.DynamicProxy;
using Framework.Core.AOP;
using Framework.Core.AOP.Cache;
using Framework.Core.Configuration;
using Framework.Core.DependencyInjection;
using Framework.Core.Extensions.DataType;
using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Core.AutoFac
{
    public class AutofacModuleRegister : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            var assemblyList = new List<Assembly>();
            var deps = DependencyContext.Default;
            var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && !lib.Type.Equals("package") && !lib.Name.Contains("Microsoft") && !lib.Name.Contains("System"));
            foreach (var lib in libs)
            {
                var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                assemblyList.Add(assembly);
            }

            if (!assemblyList.Any())
            {
                throw new Exception("未找到任何可以注入的接口,请查看项目是否依赖");
            }

            Assembly[] arrayAssembly = assemblyList.ToArray();

            // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
            var cacheType = new List<Type>();
            if (AopConfig.IsOpenCacheRedisAop)
            {
                builder.RegisterType<RedisCacheAOP>();
                cacheType.Add(typeof(RedisCacheAOP));
            }
            else
            {
                builder.RegisterType<MemoryCacheAOP>();
                cacheType.Add(typeof(MemoryCacheAOP));
            }

            #region 继承接口注入

            //注入瞬时模式
            //每次请求获取的实例不同
            builder.RegisterAssemblyTypes(arrayAssembly)
                .Where(p => typeof(ITransientDependency).IsAssignableFrom(p) && p.IsClass)
                .AsImplementedInterfaces()
                .InstancePerDependency()
                .PropertiesAutowired()
                .EnableInterfaceInterceptors();
            //  .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 若去掉该注释,可以不添加[Intercept(typeof(RedisCacheAOP))],所有依赖的类都会经过拦截器。


            //注入作用域模式
            //相同的作用域下获取的实例相同
            builder.RegisterAssemblyTypes(arrayAssembly)
                .Where(p => typeof(IScopedDependency).IsAssignableFrom(p) && p.IsClass)
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .PropertiesAutowired();
                


            //单例模式
            //每次请求进来获取的实例都相同
            builder.RegisterAssemblyTypes(arrayAssembly)
               .Where(p => typeof(ISingletonDependency).IsAssignableFrom(p) && p.IsClass)
               .AsImplementedInterfaces()
               .SingleInstance()
               .PropertiesAutowired();

            #endregion


            //// 获取 Service.dll 程序集服务,并注册
            //var assemblysServices = Assembly.LoadFrom(servicesDllFile);
            //builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces().InstancePerDependency()
            //    .PropertiesAutowired();
            //    //.EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
            //    //.InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。

            //// 获取 Repository.dll 程序集服务,并注册
            //var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
            //builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces().InstancePerDependency()
            //    .PropertiesAutowired();
        }
    }
}

 

using Castle.DynamicProxy;
using Framework.Core.AOP.Cache.Attribute;
using Framework.Core.Cache;
using Framework.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Core.AOP.Cache
{
    /// <summary>
    /// Redis AOP 拦截器
    /// </summary>
    public class RedisCacheAOP : IInterceptor
    {
        private readonly IRedisProvider _cache;

        public RedisCacheAOP(IRedisProvider redisProvider)
        {
            _cache = redisProvider;
        }
        public void Intercept(IInvocation invocation)
        {
            var method = invocation.MethodInvocationTarget ?? invocation.Method;
            //对当前方法的特性验证
            dynamic cachingAttr = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute));
            if (cachingAttr != null)
            {
                //获取自定义缓存键
                var cacheKey = CustomCacheKey(invocation);
                //注意是 string 类型,方法GetValue
                var cacheValue = _cache.GetValue(cacheKey).Result;
                if (cacheValue != null)
                {
                    //将当前获取到的缓存值,赋值给当前执行方法
                    var type = invocation.Method.ReturnType;
                    var resultTypes = type.GenericTypeArguments;
                    if (type.FullName == "System.Void")
                    {
                        return;
                    }
                    object response;
                    if (typeof(Task).IsAssignableFrom(type))
                    {
                        //返回Task<T>
                        if (resultTypes.Any())
                        {
                            dynamic temp = cacheValue.FromJson(resultTypes.First());
                            response = Task.FromResult(temp);
                        }
                        else
                        {
                            //Task 无返回方法 指定时间内不允许重新运行
                            response = Task.Yield();
                        }
                    }
                    else
                    {
                        // 核心2,要进行 ChangeType
                        response = Convert.ChangeType(_cache.Get<object>(cacheKey), type);
                    }
                    invocation.ReturnValue = response;
                    return;
                }
                //去执行当前的方法
                invocation.Proceed();

                //存入缓存
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    object response;

                    //Type type = invocation.ReturnValue?.GetType();
                    var type = invocation.Method.ReturnType;
                    if (typeof(Task).IsAssignableFrom(type))
                    {
                        var resultProperty = type.GetProperty("Result");
                        response = resultProperty.GetValue(invocation.ReturnValue);
                    }
                    else
                    {
                        response = invocation.ReturnValue;
                    }
                    if (response == null) response = string.Empty;
                    var exp = TimeSpan.FromMinutes(cachingAttr.AbsoluteExpiration);
                    _cache.Set(cacheKey, response, exp).Wait();
                }
            }
            else
            {
                invocation.Proceed();//直接执行被拦截方法
            }
        }

        //自定义缓存键
        private string CustomCacheKey(IInvocation invocation)
        {
            var typeName = invocation.TargetType.Name;
            var methodName = invocation.Method.Name;
            var methodArguments = invocation.Arguments.Select(GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个

            string key = $"{typeName}:{methodName}:";
            foreach (var param in methodArguments)
            {
                key += $"{param}:";
            }

            return key.TrimEnd(':');
        }

        //object 转 string
        private string GetArgumentValue(object arg)
        {
            if (arg is int || arg is long || arg is string)
                return arg.ToString();

            if (arg is DateTime)
                return ((DateTime)arg).ToString("yyyyMMddHHmmss");

            return "";
        }
    }
}

 

使用方式在注入到容器的接口类里面添加特性就可以实现拦截,在方法上添加缓存时效的特性就能实现改放回结果缓存

 

 

缓存特性实现类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Framework.Core.AOP.Cache.Attribute
{
    /// <summary>
    /// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
    public class CachingAttribute : System.Attribute
    {
        /// <summary>
        /// 缓存绝对过期时间(分钟)
        /// </summary>
        public int AbsoluteExpiration { get; set; } = 30;
    }
}

 

posted @ 2022-06-08 17:10  听海漫步  阅读(121)  评论(0编辑  收藏  举报