async/await 代码中的简单注入器拦截
Simple Injector interception in async/await code
我正在开始一个新项目,正在考虑使用简单注入器拦截 (https://simpleinjector.readthedocs.io/en/latest/InterceptionExtensions.html) 来跟踪方法 entry/exit 和记录参数以及 return 值等。我已经使用了这个过去的拦截器,效果很好。但是我以前的项目不是async/await。这个新项目有很多方法都是async/await,我想知道
- 此拦截器是否适用于 async/await 方法?
- 此拦截器需要进行哪些更改才能使其适用于 async/await 方法?
我知道装饰器是比拦截更好的模式,但是为我想要跟踪的每个接口编写一个装饰器并不是我期待的事情。
更新:
我已经在我的 async/await 代码中尝试过这个拦截器,它确实注入了我的跟踪代码。但是,我在应用程序的某些部分得到了奇怪的结果。我没有机会深入研究为什么禁用拦截会使其正常工作以及为什么启用拦截后它不会按预期工作。我的代码很可能有问题。
我希望如果有人已经在他们的代码中使用了这个拦截扩展,能够为我指明正确的方向。
will this interceptor work for async/await methods?
C# 中的异步代码是 Task
之上的语法糖。这意味着如果您的代码需要在 调用异步方法后做任何有用的事情,您将在返回的 Task
上调用 ContinueWith
(或使用C# 语法)。如果你在拦截器中考虑异步,你将无法在包装对象之后执行逻辑。
因此,要使这项工作正常进行,您必须明确检查包装方法是否 returns Task
,如果是这样,您应该通过挂钩 'after' 使事情异步使用 ContinueWith
.
的代码
这是我认为拦截不如使用装饰器的众多原因之一。装饰器让您的代码更简洁,避免使用反射,提供完整的编译时支持,提供更好的性能,避免依赖拦截库,并迫使您进行更可靠的应用程序设计。
也就是说,当考虑到异步性时,文档的 MonitoringInterceptor
将如下所示:
class MonitoringInterceptor : IInterceptor
{
private readonly ILogger logger;
public MonitoringInterceptor(ILogger logger) {
this.logger = logger;
}
public void Intercept(IInvocation invocation) {
var watch = Stopwatch.StartNew();
// Calls the decorated instance.
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null) {
invocation.ReturnValue = LogElapsedAsync(task, invocation, watch);
} else {
LogElapsed(invocation, watch);
}
}
private async Task LogElapsedAsync(Task task, IInvocation i, Stopwatch w) {
await task;
LogElapsed(i, w);
}
private void LogElapsed(IInvocation invocation, Stopwatch watch) {
var decoratedType = invocation.InvocationTarget.GetType();
this.logger.Log(string.Format("{0} executed in {1} ms.",
decoratedType.Name, watch.ElapsedMilliseconds));
}
}
我正在开始一个新项目,正在考虑使用简单注入器拦截 (https://simpleinjector.readthedocs.io/en/latest/InterceptionExtensions.html) 来跟踪方法 entry/exit 和记录参数以及 return 值等。我已经使用了这个过去的拦截器,效果很好。但是我以前的项目不是async/await。这个新项目有很多方法都是async/await,我想知道
- 此拦截器是否适用于 async/await 方法?
- 此拦截器需要进行哪些更改才能使其适用于 async/await 方法?
我知道装饰器是比拦截更好的模式,但是为我想要跟踪的每个接口编写一个装饰器并不是我期待的事情。
更新: 我已经在我的 async/await 代码中尝试过这个拦截器,它确实注入了我的跟踪代码。但是,我在应用程序的某些部分得到了奇怪的结果。我没有机会深入研究为什么禁用拦截会使其正常工作以及为什么启用拦截后它不会按预期工作。我的代码很可能有问题。
我希望如果有人已经在他们的代码中使用了这个拦截扩展,能够为我指明正确的方向。
will this interceptor work for async/await methods?
C# 中的异步代码是 Task
之上的语法糖。这意味着如果您的代码需要在 调用异步方法后做任何有用的事情,您将在返回的 Task
上调用 ContinueWith
(或使用C# 语法)。如果你在拦截器中考虑异步,你将无法在包装对象之后执行逻辑。
因此,要使这项工作正常进行,您必须明确检查包装方法是否 returns Task
,如果是这样,您应该通过挂钩 'after' 使事情异步使用 ContinueWith
.
这是我认为拦截不如使用装饰器的众多原因之一。装饰器让您的代码更简洁,避免使用反射,提供完整的编译时支持,提供更好的性能,避免依赖拦截库,并迫使您进行更可靠的应用程序设计。
也就是说,当考虑到异步性时,文档的 MonitoringInterceptor
将如下所示:
class MonitoringInterceptor : IInterceptor
{
private readonly ILogger logger;
public MonitoringInterceptor(ILogger logger) {
this.logger = logger;
}
public void Intercept(IInvocation invocation) {
var watch = Stopwatch.StartNew();
// Calls the decorated instance.
invocation.Proceed();
var task = invocation.ReturnValue as Task;
if (task != null) {
invocation.ReturnValue = LogElapsedAsync(task, invocation, watch);
} else {
LogElapsed(invocation, watch);
}
}
private async Task LogElapsedAsync(Task task, IInvocation i, Stopwatch w) {
await task;
LogElapsed(i, w);
}
private void LogElapsed(IInvocation invocation, Stopwatch watch) {
var decoratedType = invocation.InvocationTarget.GetType();
this.logger.Log(string.Format("{0} executed in {1} ms.",
decoratedType.Name, watch.ElapsedMilliseconds));
}
}