Har*_*til 27 c# reflection aop castle-dynamicproxy async-await
下面是从代码Intercept上实现自定义类型的方法IInterceptor的的城堡动态代理库.此片段来自此处发布的基于AOP的日志记录概念验证控制台应用程序.
public void Intercept(IInvocation invocation)
{
if (Log.IsDebugEnabled) Log.Debug(CreateInvocationLogString("Called", invocation));
try
{
invocation.Proceed();
if (Log.IsDebugEnabled)
if (invocation.Method.ReturnType != typeof(void))
Log.Debug("Returning with: " + invocation.ReturnValue);
}
catch (Exception ex)
{
if (Log.IsErrorEnabled) Log.Error(CreateInvocationLogString("ERROR", invocation), ex);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
这在常规方法调用中按预期工作,但在尝试使用async方法时(使用async/awaitC#5.0中的关键字)则不行.我相信,我也理解这背后的原因.
为了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...");
Run Code Online (Sandbox Code Playgroud)
这适用于那些async返回Task或Task<>不返回的方法,void但我很好.
在Intercept方法中需要进行哪些更改awaiter才能返回到那里而不是原始调用者?
Jon*_*eet 19
据推测,"问题"在于它只记录它正在返回一项任务 - 你想要在该任务中获得价值吗?
假设是这种情况,您仍然必须立即将任务返回给调用者 - 而不必等待它完成.如果你打破了这一点,你就会从根本上搞砸了.
但是,在将任务返回给调用者之前,应该添加一个continuation(via Task.ContinueWith),它将在任务完成时记录结果(或失败).这仍将提供结果信息,但当然您可能会在其他日志记录之后将其记录下来.您可能还希望在返回之前立即记录,从而导致如下所示的日志:
Called FooAsync
Returned from FooAsync with a task
Task from FooAsync completed, with return value 5
Run Code Online (Sandbox Code Playgroud)
将结果从任务中取出(如果成功完成)的业务必须通过反射完成,这有点痛苦 - 或者您可以使用动态类型.(无论哪种方式,它都会受到性能影响.)
Har*_*til 17
感谢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;
}
}
Run Code Online (Sandbox Code Playgroud)
尝试使用通用和清洁的解决方案来澄清:
async方法添加自定义代码作为延续任务.我认为最好的解决方案是使用dynamic关键字绕过编译器类型检查并<T>在运行时解决任务和任务之间的差异:
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;
}
Run Code Online (Sandbox Code Playgroud)