博客
关于我
在ASP.NET Core中使用AOP来简化缓存操作
阅读量:413 次
发布时间:2019-03-06

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

使用AOP简化缓存代码实现

前言

缓存是现代应用开发中至关重要的一环。优先从缓存中取数据,缓存中取不到再去数据库中取,取到了再扔进缓存中去。这一原则看似简单,但在实际项目中却常常导致重复代码的繁殖。为了解决这一问题,我们可以利用AOP(面向切面编程)来简化缓存操作。


使用Castle来实现

项目准备

  • 定义缓存接口

    定义一个ICachingProvider接口及其实现MemoryCachingProvider,用于简化缓存读写操作。

  • 定义Attribute

    创建一个QCachingAttribute,用于标注需要缓存的方法。这里使用了绝对过期时间(单位:秒)作为演示。

  • 定义辅助接口

    • IQCaching:用于标识需要缓存的方法。
    • IQCachable:用于自定义类实现,指定缓存键。
  • 实现拦截器

    创建一个QCachingInterceptor,继承IInterceptor,用于拦截需要缓存的方法。拦截器会自动处理缓存键,并在方法执行前后进行缓存读写操作。


  • 代码示例

    using Castle.Core;using Castle.MicroKernel;using Castle.MicroKernel.Extensions;using Autofac;using Autofac.Extensions.DependencyInjection;public class QCachingInterceptor : IInterceptor{    private ICachingProvider _cacheProvider;    private char _linkChar = ':';    public QCachingInterceptor(ICachingProvider cacheProvider)    {        _cacheProvider = cacheProvider;    }    public void Intercept(IInvocation invocation)    {        var qCachingAttribute = GetQCachingAttributeInfo(invocation);        if (qCachingAttribute != null)        {            ProceedCaching(invocation, qCachingAttribute);        }        else        {            invocation.Proceed();        }    }    private QCachingAttribute GetQCachingAttributeInfo(IInvocation invocation)    {        return invocation.Method.GetCustomAttributes(true)            .FirstOrDefault(x => x.GetType() == typeof(QCachingAttribute))             as QCachingAttribute;    }    private void ProceedCaching(IInvocation invocation, QCachingAttribute attribute)    {        var cacheKey = GenerateCacheKey(invocation);        var cacheValue = _cacheProvider.Get(cacheKey);        if (cacheValue != null)        {            invocation.ReturnValue = cacheValue;            return;        }        invocation.Proceed();        if (!string.IsNullOrWhiteSpace(cacheKey))        {            _cacheProvider.Set(cacheKey, invocation.ReturnValue,                 TimeSpan.FromSeconds(attribute.AbsoluteExpiration));        }    }    private string GenerateCacheKey(IInvocation invocation)    {        var typeName = invocation.TargetType.Name;        var methodName = invocation.Method.Name;        var methodArguments = FormatArgumentsToPartOfCacheKey(invocation.Arguments);        return GenerateCacheKey(typeName, methodName, methodArguments);    }    private string GenerateCacheKey(string typeName, string methodName,         IList
    parameters) { var builder = new StringBuilder(); builder.Append(typeName); builder.Append(_linkChar); builder.Append(methodName); builder.Append(_linkChar); foreach (var param in parameters) { builder.Append(param); builder.Append(_linkChar); } return builder.ToString().TrimEnd(_linkChar); } private IList
    FormatArgumentsToPartOfCacheKey( IList
    methodArguments, int maxCount = 5) { return methodArguments.Select(GetArgumentValue).Take(maxCount).ToList(); } 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"); if (arg is IQCachable) return ((IQCachable)arg).CacheKey; return null; }}

    使用方法

  • 面向接口的用法
    定义一个接口IDateTimeService,并在方法上标注QCachingAttribute
  • public interface IDateTimeService : IQCaching{    [QCaching.QCaching(AbsoluteExpiration = 10)]    string GetCurrentUtcTime();}public class DateTimeService : IDateTimeService{    public string GetCurrentUtcTime()    {        return System.DateTime.UtcNow.ToString();    }}
    1. 实例化用法
      在控制器中注入服务,并标注QCachingAttribute
    2. public class BllController : Controller{    private ILifetimeScope _scope;    private DateTimeBLL _dateTimeBLL;    public BllController(ILifetimeScope scope)    {        _scope = scope;        _dateTimeBLL = _scope.Resolve
      (); } public IActionResult Index() { return Content(_dateTimeBLL.GetCurrentUtcTime()); }}

      使用AspectCore来实现

      项目准备

    3. 定义拦截器
      创建一个QCachingInterceptor,继承AbstractInterceptor,并重写Invoke方法。
    4. using AspectCore.Core;using AspectCore.Extensions.DependencyInjection;public class QCachingInterceptor : AbstractInterceptor{    [FromContainer]    public ICachingProvider CacheProvider { get; set; }    public async override Task Invoke(AspectContext context, AspectDelegate next)    {        var qCachingAttribute = GetQCachingAttributeInfo(context.ServiceMethod);        if (qCachingAttribute != null)        {            await ProceedCaching(context, next, qCachingAttribute);        }        else        {            await next(context);        }    }    private QCachingAttribute GetQCachingAttributeInfo(IInvocation invocation)    {        return invocation.Method.GetCustomAttributes(true)            .FirstOrDefault(x => x.GetType() == typeof(QCachingAttribute))             as QCachingAttribute;    }    private async Task ProceedCaching(AspectContext context,         AspectDelegate next, QCachingAttribute attribute)    {        var cacheKey = GenerateCacheKey(context);        var cacheValue = await _cacheProvider.Get(cacheKey);        if (cacheValue != null)        {            context.ReturnValue = cacheValue;            return;        }        await next(context);        if (!string.IsNullOrWhiteSpace(cacheKey))        {            await _cacheProvider.Set(cacheKey, context.ReturnValue,                 TimeSpan.FromSeconds(attribute.AbsoluteExpiration));        }    }    private string GenerateCacheKey(IInvocation invocation)    {        var typeName = invocation.TargetType.Name;        var methodName = invocation.Method.Name;        var methodArguments = FormatArgumentsToPartOfCacheKey(invocation.Arguments);        return GenerateCacheKey(typeName, methodName, methodArguments);    }    private string GenerateCacheKey(string typeName, string methodName,         IList
      parameters) { var builder = new StringBuilder(); builder.Append(typeName); builder.Append(_linkChar); builder.Append(methodName); builder.Append(_linkChar); foreach (var param in parameters) { builder.Append(param); builder.Append(_linkChar); } return builder.ToString().TrimEnd(_linkChar); } private IList
      FormatArgumentsToPartOfCacheKey( IList
      methodArguments, int maxCount = 5) { return methodArguments.Select(GetArgumentValue).Take(maxCount).ToList(); } 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"); if (arg is IQCachable) return ((IQCachable)arg).CacheKey; return null; }}

      项目配置

      Startup中完成服务配置和拦截器注册。

      public class Startup{    public IServiceProvider ConfigureServices(IServiceCollection services)    {        services.AddMvc();        services.AddScoped
      (); services.AddScoped
      (); var assembly = this.GetType().GetTypeInfo().Assembly; AddBLLClassToServices(assembly, services); var container = services.ToServiceContainer(); container.AddType
      (); container.Configure(config => { config.Interceptors.AddTyped
      ( method => typeof(IQCache).IsAssignableFrom(method.DeclaringType)); }); return container.Build(); } public void AddBLLClassToServices(Assembly assembly, IServiceCollection services) { var types = assembly.GetTypes().ToList(); foreach (var type in types.Where(x => x.Name.EndsWith("BLL", StringComparison.OrdinalIgnoreCase) && !x.GetTypeInfo().IsAbstract())) { services.AddSingleton(type); } }}

      总结

      AOP通过简化缓存操作,显著减少了重复代码的出现。无论是使用Castle还是AspectCore,实现方式基本一致,区别主要体现在配置和使用习惯上。Castle的配置稍微复杂一些,而AspectCore则更加简洁高效。通过合理配置和注入,AOP可以有效提升应用性能,同时降低代码维护成本。

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

    你可能感兴趣的文章
    org.springframework.amqp.AmqpConnectException:java.net.ConnectException:Connection timed out:connect
    查看>>
    org.springframework.beans.factory.BeanDefinitionStoreException
    查看>>
    org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
    查看>>
    org.springframework.boot:spring boot maven plugin丢失---SpringCloud Alibaba_若依微服务框架改造_--工作笔记012
    查看>>
    SQL-CLR 类型映射 (LINQ to SQL)
    查看>>
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    查看>>
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    查看>>
    org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded
    查看>>
    org.tinygroup.serviceprocessor-服务处理器
    查看>>
    org/eclipse/jetty/server/Connector : Unsupported major.minor version 52.0
    查看>>
    org/hibernate/validator/internal/engine
    查看>>
    Orleans框架------基于Actor模型生成分布式Id
    查看>>
    SQL-36 创建一个actor_name表,将actor表中的所有first_name以及last_name导入改表。
    查看>>
    ORM sqlachemy学习
    查看>>
    Ormlite数据库
    查看>>
    orm总结
    查看>>
    ORM框架 和 面向对象编程
    查看>>
    OS X Yosemite中VMware Fusion实验环境的虚拟机文件位置备忘
    查看>>
    os.environ 没有设置环境变量
    查看>>
    os.path.join、dirname、splitext、split、makedirs、getcwd、listdir、sep等的用法
    查看>>