ASP.NET Core AOP拦截器:如何从异步方法中获取ReturnValue的Result值?
解决异步方法场景下Redis缓存切面获取invocation.ReturnValue中Result的问题
我看你这问题核心在于异步方法的返回值是Task/Task
第一步:补全异步方法判断逻辑
先确保咱们能准确识别异步方法,添加一个辅助方法:
private bool IsAsyncMethod(MethodInfo method) { var returnType = method.ReturnType; // 判断返回类型是否是Task或泛型Task<T> return returnType == typeof(Task) || (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)); }
第二步:修改Intercept方法,正确处理异步结果
核心是在invocation.Proceed()后,同步等待Task完成,再提取真正的Result值存入缓存;读取缓存时保持你的ResultFactory逻辑不变,因为它已经帮我们把结果包装成Task返回了。修改后的Intercept方法如下:
public override void Intercept(IInvocation invocation) { var methodName = $"{invocation.Method.ReflectedType.FullName}.{invocation.Method.Name}"; var method = invocation.Method; var key = KeyGenerator.GetCacheKey(invocation.Method, invocation.Arguments, "FoodThen"); var returnType = invocation.Method.ReturnType; var isExists = _redisCacheManager.IsAdd(key); var isAsync = IsAsyncMethod(method); if (isExists) { // 若GetCacheAsync是异步方法,改为cacheValue = GetCacheAsync(key).GetAwaiter().GetResult(); string cacheValue = GetCacheAsync(key); var objValue = DeserializeCache(key, cacheValue, GetUnderlyingReturnType(returnType)); invocation.ReturnValue = ResultFactory(objValue, returnType, isAsync); return; } invocation.Proceed(); // 根据是否异步处理结果缓存 if (isAsync) { // 同步等待Task完成,避免异步上下文死锁 var task = (Task)invocation.ReturnValue; task.GetAwaiter().GetResult(); // 提取Task的实际业务结果 var result = GetTaskResult(task); _redisCacheManager.Set(key, result, TimeSpan.FromMinutes(_duration)); } else { // 同步方法直接缓存返回值 _redisCacheManager.Set(key, invocation.ReturnValue, TimeSpan.FromMinutes(_duration)); } }
第三步:添加辅助方法获取Task的Result和底层返回类型
需要两个小工具方法:一个用来提取Task
// 获取Task的实际结果值 private object GetTaskResult(Task task) { var taskType = task.GetType(); // 非泛型Task没有Result,返回null if (!taskType.IsGenericType) return null; // 获取Task<T>的Result属性并取值 var resultProperty = taskType.GetProperty("Result", BindingFlags.Public | BindingFlags.Instance); return resultProperty?.GetValue(task); } // 获取方法的底层返回类型(处理Task<T>的情况) private Type GetUnderlyingReturnType(Type returnType) { if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) { // 返回Task<T>中的T类型 return returnType.GetGenericArguments()[0]; } // 同步方法或非泛型Task直接返回原类型 return returnType; }
关键修改点说明
- 缓存实际业务结果而非Task实例:异步方法Proceed后,先等待Task完成,再提取Result存入Redis,这样缓存的就是真正需要的数据。
- 读取缓存时匹配底层类型:反序列化时用GetUnderlyingReturnType拿到实际的业务类型(比如Task
对应User),确保反序列化正确。 - 同步等待Task的正确姿势:用
task.GetAwaiter().GetResult()代替task.Result,避免可能的死锁问题。
这样修改后,不管是同步还是异步方法,都能正确获取并缓存invocation.ReturnValue中的Result值了。
内容的提问来源于stack exchange,提问作者serhatyt
相关产品推荐
相关产品推荐

