.net8 AOP 实现简单的Json Redis 缓存/业务分离
场景: 在不动 业务逻辑的情况下, 实现读写缓存
这里使用的AOP的方式 在 方法上加注解, 这目前有个弊端不能传数组, 涉及到排序问题
/// <summary>
/// Json Redis缓存
/// </summary>
public class RedisInterceptorAttribute : AbstractInterceptorAttribute
{
readonly string _cacheKey;
bool _isDb = false; //是否走缓存
int _expireSeconds = -1;
public RedisInterceptorAttribute(string cacheKey, int expireSeconds = -1,bool isDb=false)
{
_cacheKey = cacheKey;
_isDb = isDb;
_expireSeconds = expireSeconds;
}
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
Type returnType = context.ServiceMethod.ReturnType;
Type genericArgument = returnType.GetGenericArguments()[0];
// 生成缓存的key
string cacheKeyWithParams = GenerateCacheKey(context);
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
//通过类型获取缓存方法,并且调用 GetAsync 方法
MethodInfo method = this.GetType().GetMethod("GetAsync", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MethodInfo genericMethod = method.MakeGenericMethod(genericArgument);
var cacheTask = (Task)genericMethod.Invoke(this, new object[] { cacheKeyWithParams });
await cacheTask.ConfigureAwait(true);
var resultProperty = cacheTask.GetType().GetProperty("Result");
var cacheResult = resultProperty.GetValue(cacheTask);
if (cacheResult != null)
{
context.ReturnValue = ConvertToReturnType(cacheResult, returnType);
// 设置缓存的逻辑
await Task.CompletedTask;
}
else
{
await context.Invoke(next);
// 设置缓存
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
MethodInfo setMethod = this.GetType().GetMethod("SetAsync", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MethodInfo genericSetMethod = setMethod.MakeGenericMethod(genericArgument);
var resultProperty1 = context.ReturnValue.GetType().GetProperty("Result");
var result = resultProperty1.GetValue(context.ReturnValue);
await (Task)genericSetMethod.Invoke(this, new object[] { cacheKeyWithParams, result });
}
}
}
else
{
await next(context);
}
}
private string GenerateCacheKey(AspectContext context)
{
var keyBuilder = new System.Text.StringBuilder(_cacheKey);
foreach (var parameter in context.Parameters)
{
keyBuilder.Append(":").Append(FormatParameter(parameter));
}
return keyBuilder.ToString();
}
/// <summary>
/// 格式化参数(类似url过滤一下数据,: 会影响阅读)
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
private string FormatParameter(object parameter)
{
return JsonConvert.SerializeObject(parameter).Replace(":", "=").Replace("{", "").Replace("}", "").Replace("\"", "").Replace(",", "&");
}
/// <summary>
/// 将返回值转换为原有类型
/// </summary>
/// <param name="result"></param>
/// <param name="returnType"></param>
/// <returns></returns>
private object ConvertToReturnType(object result, Type returnType)
{
Type taskResultType = returnType.GetGenericArguments()[0];
return typeof(Task).GetMethod(nameof(Task.FromResult))
.MakeGenericMethod(taskResultType)
.Invoke(null, new[] { result });
}
async Task<T> GetAsync<T>(string key)
{
var result = await RedisHelper.GetAsync<T>(key);
return result;
}
async Task SetAsync<T>(string cacheKey, T data)
{
if (data != null)
{
await RedisHelper.SetAsync(cacheKey, data, _expireSeconds);
}
}
}
这样就简单实现了
只需要在方法上加注解, 一定要加virtual
//一定要加virtual
[RedisInterceptor("school")]
public virtual async Task<List<string>> SelectAsync(Guid[] schoolId)
{
///业务
return new();
}
QQ群: 929412850 (Byte.Core 框架交流群)
未经作者同意,禁止转发