写点什么

AOP 的姿势之 简化 MemoryCache 使用方式

用户头像
八苦-瞿昙
关注
发布于: 2020 年 12 月 27 日

0. 前言

之前写了几篇文章介绍了一些 AOP 的知识,

但是还没有亮出来 AOP 的姿势,

也许姿势漂亮一点,

大家会对 AOP 有点兴趣

内容大致会分为如下几篇:(毕竟人懒,一下子写完太累了,没有动力)

  1. AOP 的姿势之 简化 MemoryCache 使用方式

  2. AOP 的姿势之 简化混用 MemoryCache 和 DistributedCache 使用方式

  3. AOP 的姿势之 如何把 HttpClient 变为声明式

至于 AOP 框架在这儿示例依然会使用我自己基于 emit 实现的动态代理 AOP 框架: https://github.com/fs7744/Norns.Urd

毕竟是自己写的,魔改/加功能都很方便,

万一万一大家如果有疑问,(虽然大概不会有),我也好回答, (当然如果大家认可,在 github 给个 star,就实在是太让人开心了)

1. 正文

1.1 回顾 MemoryCache 如何使用

var cache = ServiceProvider.GetRequiredService<IMemoryCache>();var r = await cache.GetOrCreateAsync(cacheKey, async e =>            {                var rr = await do();                e.AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow;                return rr;            });
复制代码

MemoryCache 本身已经被封装到如此简单就可以使用了

但是呢,每次我们使用的时候依然要这样重复写类似的代码

当然我们都是拥有超强的 ctrl+c 和 ctrl+v 能力,

这点点重复代码都是些毛毛雨啦,

上 w 行代码一把梭都是小场面了,

不过呢,这样的代码写的和在校的学生一样,

怎么能体现我们混迹江湖,加班数十载的逼格呢?

我们要让这些在校学生/实习生看不懂我们的代码,

让他们看不到GetOrCreateAsync

让他们调试的时候 do() 里面的断点跑不到

这样我们才能展示出扫地僧的实力:来,小朋友,我来教你新姿势



1.2 逼格启航

1.2.1 逼格核心 - 拦截器

在 Norns.Urd 中,Interceptor 拦截器是用户可以在方法插入自己的逻辑的核心。

标准结构为IInterceptor

public interface IInterceptor{    // 用户可以通过Order自定义拦截器顺序,排序方式为ASC,全局拦截器和显示拦截器都会列入排序中    int Order { get; }
// 同步拦截方法 void Invoke(AspectContext context, AspectDelegate next);
// 异步拦截方法 Task InvokeAsync(AspectContext context, AsyncAspectDelegate next);
// 可以设置拦截器如何选择过滤是否拦截方法,除了这里还有NonAspectAttribute 和全局的NonPredicates可以影响过滤 bool CanAspect(MethodInfo method);}
复制代码

这里我们为了大家理解简单,就使用最简单的方式来做 : 使用 AbstractInterceptorAttribute

一个非常简单的例子就如下了:

    public class CacheAttribute : AbstractInterceptorAttribute    {        private readonly TimeSpan absoluteExpirationRelativeToNow;        private readonly string cacheKey;
// 为了简单,缓存策略我们就先只支持TTL 存活固定时间 public CacheAttribute(string cacheKey, string absoluteExpirationRelativeToNow) { this.cacheKey = cacheKey; this.absoluteExpirationRelativeToNow = TimeSpan.Parse(absoluteExpirationRelativeToNow); }
public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next) { // 整个代码基本和我们直接使用 MemoryCache 一样 var cache = context.ServiceProvider.GetRequiredService<IMemoryCache>(); var r = await cache.GetOrCreateAsync(cacheKey, async e => { await next(context); // 所以真正实现的方法逻辑都在 next 中,所以调用它就好了 e.AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow; return context.ReturnValue; // 结果都在ReturnValue , 这里为了简单,就不写 void / Task<T> / ValueTask<T> 等等 各种返回值的兼容代码了 }); context.ReturnValue = r; // 设置 ReturnValue, 由于缓存有效期内, next不会被调用, 所以ReturnValue不会有值,我们需要将缓存结果设置到 ReturnValue } }
复制代码

1.2.2 测试一下

 public interface ITestCacheClient    {        string Say(string v);    }
public class TestCacheClient : ITestCacheClient { public string Say(string v) => v; }
static class Program { static void Main(string[] args) { var client = new ServiceCollection() .AddMemoryCache() .AddSingleton<ITestCacheClient, TestCacheClient>() .ConfigureAop() .BuildServiceProvider() .GetRequiredService<ITestCacheClient>(); Console.WriteLine(client.Say("Hello World!")); Console.WriteLine(client.Say("Hello Two!")); Thread.Sleep(3000); Console.WriteLine(client.Say("Hello Two!")); } }
复制代码

Console 结果

Hello World!Hello Two!Hello Two!
复制代码

加上缓存设置:

    public class TestCacheClient : ITestCacheClient    {        [Cache(nameof(Say), "00:00:03")]        public string Say(string v) => v;    }
复制代码

再次测试的 Console 结果

Hello World!Hello World!Hello Two!
复制代码

例子代码都在 https://github.com/fs7744/AopDemoList/tree/master/MakeMemoryChacheSimple

处理情况更全面的例子在 https://github.com/fs7744/Norns.Urd/tree/main/src

祝大家都能愉快被叫 大神 nb。


发布于: 2020 年 12 月 27 日阅读数: 17
用户头像

八苦-瞿昙

关注

一个假和尚,不懂人情世故。 2018.11.23 加入

会点点技术,能写些代码,只爱静静。 g hub: https://github.com/fs7744 黑历史:https://www.cnblogs.com/fs7744

评论

发布
暂无评论
AOP的姿势之 简化 MemoryCache 使用方式