通过 DynamicProxy 拦截 returns generic Task<> 的异步方法
Intercept async method that returns generic Task<> via DynamicProxy
我的问题与此有关 post Intercept the call to an async method using DynamicProxy
我想实现与 returns Task
或 Task<T>
结果的异步方法一起工作的拦截器。
我将下一个代码用于 return ContinueWith
结果(以便调用方方法在拦截器完成工作时等待)
var task = invocation.ReturnValue as Task;
invocation.ReturnValue = task.ContinueWith(c =>
{ code that should execute after method finish });
上面的代码对于 Task
结果工作正常,但是如果 Task<T>
结果 ContinueWith
会将 return 类型从 Task<T>
更改为 Task
.
我需要调用 returns Task<T>
的重载方法 ContinueWith,但为此我需要将 invocation.ReturnValue
转换为 Task<T>
我没有找到以任何方式动态投射它的方法。
有人知道怎么做吗?
我也试过通过反射调用这个方法,但是参数是labmda函数,不能直接传进去
经过广泛的研究,我能够创建一个适用于拦截同步方法以及异步任务和异步任务< TResult > 的解决方案。
这是我使用 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
}
}
更好的解决方案是使用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;
}
由于需要拦截返回 Task<TResult>
的方法,我创建了 Castle.Core
的扩展以简化流程。
包可在 NuGet 下载。
解决方案主要基于 @silas-reinagel, but simplifies it by providing a new interface to implement IAsyncInterceptor 的答案。还有进一步的抽象使拦截类似于实现 Interceptor
.
有关详细信息,请参阅项目的 readme。
我这样做过:
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);
}
@Silas Reinagel 和@thepirat000 的解决方案对我不起作用,我使用@James Skimming 的Castle.Core.AsyncInterceptor
解决方案也没有成功。
在我的例子中,我拦截了一个返回 Task
的异步方法,只有在 invocation.Proceed()
期间没有异常时它才应该执行 "after invocation.Proceed() code"。最后我使用了@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()
{
}
}
我的问题与此有关 post Intercept the call to an async method using DynamicProxy
我想实现与 returns Task
或 Task<T>
结果的异步方法一起工作的拦截器。
我将下一个代码用于 return ContinueWith
结果(以便调用方方法在拦截器完成工作时等待)
var task = invocation.ReturnValue as Task;
invocation.ReturnValue = task.ContinueWith(c =>
{ code that should execute after method finish });
上面的代码对于 Task
结果工作正常,但是如果 Task<T>
结果 ContinueWith
会将 return 类型从 Task<T>
更改为 Task
.
我需要调用 returns Task<T>
的重载方法 ContinueWith,但为此我需要将 invocation.ReturnValue
转换为 Task<T>
我没有找到以任何方式动态投射它的方法。 有人知道怎么做吗?
我也试过通过反射调用这个方法,但是参数是labmda函数,不能直接传进去
经过广泛的研究,我能够创建一个适用于拦截同步方法以及异步任务和异步任务< TResult > 的解决方案。
这是我使用 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
}
}
更好的解决方案是使用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;
}
由于需要拦截返回 Task<TResult>
的方法,我创建了 Castle.Core
的扩展以简化流程。
包可在 NuGet 下载。
解决方案主要基于 @silas-reinagel, but simplifies it by providing a new interface to implement IAsyncInterceptor 的答案。还有进一步的抽象使拦截类似于实现 Interceptor
.
有关详细信息,请参阅项目的 readme。
我这样做过:
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);
}
@Silas Reinagel 和@thepirat000 的解决方案对我不起作用,我使用@James Skimming 的Castle.Core.AsyncInterceptor
解决方案也没有成功。
在我的例子中,我拦截了一个返回 Task
的异步方法,只有在 invocation.Proceed()
期间没有异常时它才应该执行 "after invocation.Proceed() code"。最后我使用了@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()
{
}
}