C# 使用DynamicProxy拦截对异步方法的调用
下面是实现库的C# 使用DynamicProxy拦截对异步方法的调用,c#,reflection,aop,async-await,castle-dynamicproxy,C#,Reflection,Aop,Async Await,Castle Dynamicproxy,下面是实现库的IInterceptor的自定义类型上的Intercept方法的代码。此代码段来自发布的基于日志的概念验证控制台应用程序 这在常规方法调用中可以正常工作,但在使用async方法(使用C#5.0中的async/await关键字)尝试时却无法正常工作。我相信,我也理解这背后的原因 为了使async/await工作,编译器在后台将方法的函数体添加到状态机中,一旦遇到第一个无法同步完成的awaitable表达式,控件就会返回给调用者 此外,我们还可以查询返回类型并确定是否正在处理一个asy
IInterceptor
的自定义类型上的Intercept
方法的代码。此代码段来自发布的基于日志的概念验证控制台应用程序
这在常规方法调用中可以正常工作,但在使用async
方法(使用C#5.0中的async/await
关键字)尝试时却无法正常工作。我相信,我也理解这背后的原因
为了使async/await
工作,编译器在后台将方法的函数体添加到状态机中,一旦遇到第一个无法同步完成的awaitable
表达式,控件就会返回给调用者
此外,我们还可以查询返回类型并确定是否正在处理一个async
方法,如下所示:
if (invocation.Method.ReturnType == typeof(Task) ||
(invocation.Method.ReturnType.IsGenericType &&
invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)))
Log.Info("Asynchronous method found...");
if(invocation.Method.ReturnType==typeof(Task)|
(invocation.Method.ReturnType.IsGenericType&&
invocation.Method.ReturnType.GetGenericTypeDefinition()==typeof(任务)))
Log.Info(“找到异步方法…”);
这只适用于那些返回Task
或Task
且不返回void
的async
方法,但我同意这一点
在Intercept
方法中必须进行哪些更改,以使等待者返回到那里,而不是原始调用者 大概“问题”在于,它只是在记录它返回的任务,而您想要该任务中的值
假设是这样,您仍然必须立即将任务返回给调用者,而不必等待任务完成。如果你打破了这一点,你就从根本上把事情搞砸了
但是,在将任务返回给调用者之前,应该添加一个延续(via),它将在任务完成时记录结果(或失败)。这仍然会给出结果信息,但当然,在其他一些日志记录之后,您可能会对其进行日志记录。您可能还希望在返回之前立即进行日志记录,从而生成类似以下内容的日志:
if (invocation.Method.ReturnType == typeof(Task) ||
(invocation.Method.ReturnType.IsGenericType &&
invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)))
Log.Info("Asynchronous method found...");
称为fooancy
使用任务从FooAsync返回
来自FooAsync的任务已完成,返回值为5
从任务中获取结果(如果任务成功完成)的工作必须通过反射来完成,这有点麻烦——或者您可以使用动态键入。(无论哪种方式,它都会有一点性能上的冲击。)多亏了Jon的回答,这就是我最终得到的结果:
public void Intercept(IInvocation invocation)
{
if (Log.IsDebugEnabled) Log.Debug(CreateInvocationLogString("Called", invocation));
try
{
invocation.Proceed();
if (Log.IsDebugEnabled)
{
var returnType = invocation.Method.ReturnType;
if (returnType != typeof(void))
{
var returnValue = invocation.ReturnValue;
if (returnType == typeof(Task))
{
Log.Debug("Returning with a task.");
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
Log.Debug("Returning with a generic task.");
var task = (Task)returnValue;
task.ContinueWith((antecedent) =>
{
var taskDescriptor = CreateInvocationLogString("Task from", invocation);
var result =
antecedent.GetType()
.GetProperty("Result")
.GetValue(antecedent, null);
Log.Debug(taskDescriptor + " returning with: " + result);
});
}
else
{
Log.Debug("Returning with: " + returnValue);
}
}
}
}
catch (Exception ex)
{
if (Log.IsErrorEnabled) Log.Error(CreateInvocationLogString("ERROR", invocation), ex);
throw;
}
}
公共无效截获(IInvocation调用)
{
if(Log.IsDebugEnabled)Log.Debug(CreateInvocationLogString(“调用”),invocation));
尝试
{
invocation.procedure();
if(Log.IsDebugEnabled)
{
var returnType=invocation.Method.returnType;
if(返回类型!=类型(无效))
{
var returnValue=invocation.returnValue;
if(returnType==typeof(任务))
{
Debug(“带任务返回”);
}
else if(returnType.IsGenericType&&returnType.GetGenericTypeDefinition()==typeof(任务))
{
Debug(“使用一般任务返回”);
var task=(task)returnValue;
task.ContinueWith((先行项)=>
{
var tasksdescriptor=CreateInvocationLogString(“任务来源”,调用);
var结果=
antecedent.GetType()
.GetProperty(“结果”)
.GetValue(先行项,空);
调试(taskDescriptor+“返回:”+结果);
});
}
其他的
{
Log.Debug(“返回值为:”+returnValue);
}
}
}
}
捕获(例外情况除外)
{
if(Log.IsErrorEnabled)Log.Error(CreateInvocationLogString(“Error”,invocation),ex);
投掷;
}
}
我的2美分:
已经正确地确定,对于异步
方法,拦截器的目的是通过延续“增强”调用返回的任务
现在,正是这个任务的延续必须被返回,才能完成拦截器的任务
因此,基于上述讨论和示例,这对于常规方法以及“原始”async Task
方法都非常有效
public virtual void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null)
{
invocation.ReturnValue = task.ContinueWith(t => {
if (t.IsFaulted)
OnException(invocation, t.Exception);
});
}
}
catch (Exception ex)
{
OnException(invocation, ex);
}
}
public virtual void OnException(IInvocation invocation, Exception exception)
{
...
}
但是当处理异步任务
方法时,上述方法会错误地将拦截返回的任务类型从任务
更改为常规任务
请注意,我们调用的是Task.ContinueWith()
,而不是Task.ContinueWith()
,这是我们要调用的方法
这将是最终等待此类拦截时产生的例外情况:
System.InvalidCastException:无法将类型为“System.Threading.Tasks.ContinuationTaskFromTask”的对象强制转换为类型为“System.Threading.Tasks.Task`1”的对象
下面是正确处理异步方法的异步侦听器适配器实现
abstract class AsyncInterceptor : IInterceptor
{
class TaskCompletionSourceMethodMarkerAttribute : Attribute
{
}
private static readonly MethodInfo _taskCompletionSourceMethod = typeof(AsyncInterceptor)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.Single(x => x.GetCustomAttributes(typeof(TaskCompletionSourceMethodMarkerAttribute)).Any());
protected virtual Task<Object> InterceptAsync(Object target, MethodBase method, object[] arguments, Func<Task<Object>> proceed)
{
return proceed();
}
protected virtual void Intercept(Object target, MethodBase method, object[] arguments, Action proceed)
{
proceed();
}
[TaskCompletionSourceMethodMarker]
Task<TResult> TaskCompletionSource<TResult>(IInvocation invocation)
{
var tcs = new TaskCompletionSource<TResult>();
var task = InterceptAsync(invocation.InvocationTarget, invocation.Method, invocation.Arguments, () =>
{
var task2 = (Task)invocation.Method.Invoke(invocation.InvocationTarget, invocation.Arguments);
var tcs2 = new TaskCompletionSource<Object>();
task2.ContinueWith(x =>
{
if (x.IsFaulted)
{
tcs2.SetException(x.Exception);
return;
}
dynamic dynamicTask = task2;
Object result = dynamicTask.Result;
tcs2.SetResult(result);
});
return tcs2.Task;
});
task.ContinueWith(x =>
{
if (x.IsFaulted)
{
tcs.SetException(x.Exception);
return;
}
tcs.SetResult((TResult)x.Result);
});
return tcs.Task;
}
void IInterceptor.Intercept(IInvocation invocation)
{
if (!typeof(Task).IsAssignableFrom(invocation.Method.ReturnType))
{
Intercept(invocation.InvocationTarget, invocation.Method, invocation.Arguments, invocation.Proceed);
return;
}
var returnType = invocation.Method.ReturnType.IsGenericType ? invocation.Method.ReturnType.GetGenericArguments()[0] : typeof(object);
var method = _taskCompletionSourceMethod.MakeGenericMethod(returnType);
invocation.ReturnValue = method.Invoke(this, new object[] { invocation });
}
}
抽象类异步侦听器:IInterceptor
{
类TaskCompletionSourceMethodMarkerAttribute:属性
{
}
私有静态只读MethodInfo\u taskCompletionSourceMethod=typeof(异步侦听器)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.单人(x=
class TestInterceptor : AsyncInterceptor
{
protected override async Task<Object> InterceptAsync(object target, MethodBase method, object[] arguments, Func<Task<object>> proceed)
{
await Task.Delay(5000);
var result = await proceed();
return DateTime.Now.Ticks % 2 == 0 ? 10000 :result;
}
}
void IInterceptor.Intercept(IInvocation invocation) {
try {
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null && task.IsFaulted) throw task.Exception;
}
catch {
throw;
}
}
tcs2.SetException(x.Exception);
x.Exception.Handle(ex => { tcs2.SetException(ex); return true; });
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 logging here, as continuation work for Task...
}
private static async Task<T> InterceptAsync<T>(Task<T> task)
{
T result = await task.ConfigureAwait(false);
// do the logging here, as continuation work for Task<T>...
return result;
}