我的问题与此帖Intercept the call to an async method using DynamicProxy
有关我想实现拦截器,该拦截器使用返回Task
或Task<T>
结果的异步方法。
我使用下一个代码返回ContinueWith
结果(以便在拦截器完成工作时调用方法等待)
var task = invocation.ReturnValue as Task;
invocation.ReturnValue = task.ContinueWith(c =>
{ code that should execute after method finish });
以上代码适用于Task
结果,但如果Task<T>
结果ContinueWith
将返回类型从Task<T>
更改为Task
。
我需要调用返回Task<T>
的重载方法ContinueWith,但为此我需要将invocation.ReturnValue
强制转换为Task<T>
我没有找到以任何方式动态投射它的方法。 有谁知道如何制作它?
我也尝试通过反射调用此方法,但参数是labmda函数,不能直接传递。
答案 0 :(得分:21)
经过广泛的研究,我能够创建一个解决方案,用于拦截同步方法以及异步任务和异步任务&lt; TResult&gt;。
这是我使用Castle Dynamic Proxy处理所有这些方法类型的异常处理拦截器的代码。这种模式适用于进行任何你想要的拦截。对于标准的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<T> HandleAsyncWithResult<T>(Task<T> 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
}
}
答案 1 :(得分:11)
更好的解决方案是使用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<T> InterceptAsync<T>(Task<T> task)
{
T result = await task.ConfigureAwait(false);
// do the continuation work for Task<T>...
return result;
}
答案 2 :(得分:5)
需要拦截返回Task<TResult>
的方法,我创建了Castle.Core
的扩展,简化了流程。
该套餐可在NuGet上下载。
解决方案主要基于@silas-reinagel的答案,但通过提供实现IAsyncInterceptor的新界面来简化解决方案。还有进一步的抽象,使拦截类似于实施Interceptor
。
有关详细信息,请参阅项目的readme。
答案 3 :(得分:0)
我已经这样做了:
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);
}
答案 4 :(得分:0)
@Silas Reinagel和@ thepirat000的解决方案对我不起作用,并且我无法成功使用@James Skimming的Castle.Core.AsyncInterceptor
解决方案。
在我的情况下,我拦截了一个返回Task
的异步方法,并且只有在invocation.Proceed()
期间没有异常的情况下,它才应执行“ invocation.Proceed()代码之后”。最后,我使用了@James Skimming的code sample,因此该解决方案仅适用于拦截返回Task
而不是Task<TResult>
的异步方法:
public void Intercept(IInvocation invocation)
{
_stepPriorInvocation();
invocation.Proceed();
Func<Task> continuation = async () =>
{
await (Task)invocation.ReturnValue;
_stepAfterSuccessfulInvocation();
};
invocation.ReturnValue = continuation();
void _stepPriorInvocation()
{
}
void _stepAfterSuccessfulInvocation()
{
}
}