拦截异步方法,通过DynamicProxy返回通用Task

我的问题与这篇文章有关拦截使用DynamicProxy调用异步方法

我想实现拦截器,该拦截器使用返回TaskTask结果的异步方法。

我使用下一个代码返回ContinueWith结果(以便在拦截器完成工作时调用方法等待)

 var task = invocation.ReturnValue as Task; invocation.ReturnValue = task.ContinueWith(c => { code that should execute after method finish }); 

上面的代码适用于Task结果,但是在Task结果的情况下, ContinueWith会将返回类型从Task更改为Task 。 我需要调用重载方法ContinueWith返回Task ,但为此我需要将invocation.ReturnValueTask

我没有找到以任何方式动态投射它的方法。 有谁知道如何制作它?

我也尝试通过reflection调用此方法,但参数是labmda函数,不能直接传递。

经过广泛的研究,我能够创建一个解决方案,用于拦截同步方法以及异步任务和异步任务

下面是我使用Castle Dynamic Proxy处理所有这些方法类型的exception处理拦截器的代码。 这种模式适用于进行任何你想要的拦截。 标准的BeforeInvoke / AfterInvoke操作的语法会更清晰,但概念应该相同。

(其他注意:示例中的IExceptionHandler接口是自定义类型,而不是公共对象。)

  private class AsyncExceptionHandlingInterceptor : IInterceptor { private static readonly MethodInfo handleAsyncMethodInfo = typeof(AsyncExceptionHandlingInterceptor).GetMethod("HandleAsyncWithResult", BindingFlags.Instance | BindingFlags.NonPublic); private readonly IExceptionHandler _handler; public AsyncExceptionHandlingInterceptor(IExceptionHandler handler) { _handler = handler; } public void Intercept(IInvocation invocation) { var delegateType = GetDelegateType(invocation); if (delegateType == MethodType.Synchronous) { _handler.HandleExceptions(() => invocation.Proceed()); } if (delegateType == MethodType.AsyncAction) { invocation.Proceed(); invocation.ReturnValue = HandleAsync((Task)invocation.ReturnValue); } if (delegateType == MethodType.AsyncFunction) { invocation.Proceed(); ExecuteHandleAsyncWithResultUsingReflection(invocation); } } private void ExecuteHandleAsyncWithResultUsingReflection(IInvocation invocation) { var resultType = invocation.Method.ReturnType.GetGenericArguments()[0]; var mi = handleAsyncMethodInfo.MakeGenericMethod(resultType); invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue }); } private async Task HandleAsync(Task task) { await _handler.HandleExceptions(async () => await task); } private async Task HandleAsyncWithResult(Task task) { return await _handler.HandleExceptions(async () => await task); } private MethodType GetDelegateType(IInvocation invocation) { var returnType = invocation.Method.ReturnType; if (returnType == typeof(Task)) return MethodType.AsyncAction; if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) return MethodType.AsyncFunction; return MethodType.Synchronous; } private enum MethodType { Synchronous, AsyncAction, AsyncFunction } } 

更好的解决方案是使用dynamic关键字绕过编译器类型检查并在运行时解决操作:

 public void Intercept(IInvocation invocation) { invocation.Proceed(); var method = invocation.MethodInvocationTarget; var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null; if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType)) { invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue); } } private static async Task InterceptAsync(Task task) { await task.ConfigureAwait(false); // do the continuation work for Task... } private static async Task InterceptAsync(Task task) { T result = await task.ConfigureAwait(false); // do the continuation work for Task... return result; } 

需要拦截返回Task ,我创建了Castle.Core的扩展,简化了过程。

Castle.Core.AsyncInterceptor

该软件包可在NuGet上下载。

该解决方案主要基于@ silas-reinagel的答案,但通过提供实现IAsyncInterceptor的新接口简化了它。 还有进一步的抽象,使拦截类似于实现Interceptor

有关更多详细信息,请参阅项目自述文件 。

我这样做了:

 invocation.Proceed(); object response; Type type = invocation.ReturnValue?.GetType(); if (type != null && typeof(Task).IsAssignableFrom(type)) { var resultProperty = type.GetProperty("Result"); response = resultProperty.GetValue(invocation.ReturnValue); }