ASP.NET Core中IMemoryCache迁移Redis缓存的类型转换问题求解
问题本质
IMemoryCache属于进程内缓存,存储的是CLR托管对象的直接引用,写入时保留了对象的完整类型信息,读取时不需要做类型转换,自然可以直接转成对应业务类型。
Redis属于跨进程分布式缓存,所有写入的数据都需要序列化成字符串/二进制格式存储,读取时拿到的是原始序列化结果,没有携带原始CLR类型信息,如果不做反序列化处理,自然无法直接转换为业务类型,这是分布式缓存替换内存缓存实现通用AOP拦截时的共性问题。
实现方案
核心逻辑是利用拦截器上下文中可以直接获取被拦截方法返回类型的特性,不需要在缓存特性上提前声明类型,读取缓存时直接以方法返回类型为目标做反序列化即可,分两步改造:
- 改造缓存服务的读取方法,支持传入目标类型完成反序列化
/// <summary> /// 读取缓存并反序列化为指定类型 /// </summary> public object Get(string key, Type targetType) { var redisValue = _client.GetDatabase().StringGet(key); if (!redisValue.HasValue) { return null; } // 序列化配置需要和写入时保持完全一致 return JsonConvert.DeserializeObject(redisValue.ToString(), targetType, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); } /// <summary> /// 泛型读取重载,供非AOP场景直接调用 /// </summary> public T Get<T>(string key) { var result = Get(key, typeof(T)); return result == null ? default : (T)result; }
原有Set方法不需要大改,只需要补充空值判断即可,避免空对象序列化异常。
- 修正AOP拦截器逻辑,缓存命中时传入方法返回类型做反序列化,同时修复原拦截器未给返回值赋值的问题
public override void Intercept(IInvocation invocation) { var methodName = $"{invocation.Method.ReflectedType.FullName}.{invocation.Method.Name}"; var arguments = invocation.Arguments.ToList(); var key = $"{methodName}({string.Join(",", arguments.Select(x => x != null ? JsonConvert.SerializeObject(x, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }) : "<Null>"))})"; if (_cacheManager.IsAdd(key)) { // 从拦截上下文直接获取当前方法的返回值类型,不需要提前硬编码 Type methodReturnType = invocation.Method.ReturnType; var cacheValue = _cacheManager.Get(key, methodReturnType); // 必须将反序列化后的结果赋值给Invocation的ReturnValue属性,调用方才能拿到缓存值 invocation.ReturnValue = cacheValue; return; } invocation.Proceed(); _cacheManager.Set(key, invocation.ReturnValue, _duration); }
注意事项
- 如果项目中有异步方法(返回
Task<T>)需要加缓存拦截,需要额外判断返回类型,提取Task<>的泛型参数作为反序列化目标类型,反序列化完成后包装为对应类型的Task结果再赋值给ReturnValue,否则会出现类型不匹配错误。可以直接借助Castle.Core的异步拦截扩展简化处理,不需要手动实现Task包装逻辑。 - 序列化、反序列化两端的配置必须完全一致,包括时间格式、字段命名策略、循环引用处理、空值处理规则等,否则会出现字段丢失、时间格式错误等问题。
- 建议在写入缓存时对值类型、空引用做特殊判断,避免序列化出无意义的空字符串、"null"字符串导致反序列化异常。
- 后续如果需要替换序列化方案(比如切换为Protobuf、MessagePack获得更高性能),只需要修改缓存服务内部的序列化/反序列化逻辑,拦截器层和业务层代码不需要任何调整,完全符合AOP的解耦要求。
内容的提问来源于stack exchange,提问作者serhatyt
相关产品推荐
相关产品推荐

