下面是来自 Intercept
的代码实现 IInterceptor
的自定义类型上的方法的 Castle Dynamic Proxy图书馆。此片段来自 AOP发布的基于日志记录的概念验证控制台应用程序 here .
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;
}
}
这在常规方法调用上按预期工作,但在尝试使用 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...");
这只适用于那些 async
返回 Task
的方法或 Task<>
而不是 void
但我对此没有意见。
必须在 Intercept
中进行哪些更改?方法使得 awaiter
会回到那里而不是原来的来电者?
最佳答案
大概“问题”是它只是记录它正在返回一个任务 - 而您想要该任务中的值?
假设是这种情况,您仍然必须立即将任务返回给调用者,而不是等待它完成。如果你打破了它,你就会从根本上把事情搞砸。
但是,在将任务返回给调用者之前,您应该添加一个延续(通过 Task.ContinueWith
),它将在任务完成时记录结果(或失败)。这仍然会提供结果信息,但当然您可能会在其他一些日志记录之后记录它。您可能还希望在返回之前立即记录日志,从而生成如下所示的日志:
Called FooAsync
Returned from FooAsync with a task
Task from FooAsync completed, with return value 5
从任务中获取结果(如果它成功完成)的业务必须通过反射来完成,这有点痛苦 - 或者您可以使用动态类型。 (无论哪种方式都会对性能造成一些影响。)
关于c# - 使用 DynamicProxy 拦截对异步方法的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14288075/