c# - Autofac拦截异步执行顺序

标签 c# async-await autofac interception

所以我有这个类:

public class MappingBootstrap : IMappingBootstrap
{
    public virtual async Task Map()
    {
        // Order is very important

        await this.mapper1.Map();

        await this.mapper2.Map();

        await this.mapper3.Map();

        await this.mapper4.Map();
    }
}

我有 Autofac 拦截器:

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        var methodReference = Guid.NewGuid();
        Console.WriteLine($"Calling {invocation?.Method?.DeclaringType?.Name}.{invocation?.Method?.Name} : {methodReference}");

        var startNew = Stopwatch.StartNew();

        invocation?.Proceed();

        startNew.Stop();

        Console.WriteLine($"{methodReference} : Done, time taken: {startNew.ElapsedMilliseconds}ms");
    }
}

这会产生输出:

Calling IMapperBootstrap.Map : 54425559-71fe-4f23-ab47-d0f3371ec819
Calling IMapper1.Map : 51babb34-fa83-42ed-84e7-a1e979528116
51babb34-fa83-42ed-84e7-a1e979528116 : Done, time taken: 219ms
54425559-71fe-4f23-ab47-d0f3371ec819 : Done, time taken: 221ms
Calling IMapper2.Map : 41c812a2-d82d-48f6-9b8d-139b52eb28e3
41c812a2-d82d-48f6-9b8d-139b52eb28e3 : Done, time taken: 9ms
Calling IMapper3.Map : c91bed04-8f86-47d3-a35a-417e354c2c5f
c91bed04-8f86-47d3-a35a-417e354c2c5f : Done, time taken: 994ms
Calling IMapper4.Map : 035cad27-1ba8-4bd1-b85f-396f64998d97
035cad27-1ba8-4bd1-b85f-396f64998d97 : Done, time taken: 18ms

如您所见,MappingBoostrap.Map 在第一个 Mapper1.Map 之后完成,而不是我所期望的,即所有功能都已完成。 为什么?


Autofac 配置:

builder.Register(context => new LoggingInterceptor());

builder
    .RegisterAssemblyTypes(typeof(Bootstrapper).Assembly)
    .Where(x => x.Namespace.Contains("Mapping"))
    .AsImplementedInterfaces()
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(LoggingInterceptor));

最佳答案

Why?

当你像这样调用一个异步方法时:

mapping.Map();

它只启动方法。这是 how asynchronous methods work (正如我在我的博客上解释的那样)。如果您 await 异步方法返回的任务,则当前方法将暂停,直到异步方法完成:

await mapping.Map();

拦截的情况下,理想的解决方案是让ProceedIntercept 方法异步:

public async Task InterceptAsync(IInvocation invocation)
{
  ...
  await invocation?.ProceedAsync();
  ...
}

不幸的是,Autofac 没有对异步方法的内置理解,因此这是不可能的。相反,您必须调用 Proceed,这只会启动异步方法。异步方法返回一个 Task,代表该方法的执行。要 Hook 方法的完成,您应该替换那个 Task 为您自己的任务。

对于普通的 Task 返回方法,您可以使用这样的方法:

public void Intercept(IInvocation invocation)
{
  var methodReference = Guid.NewGuid();
  Console.WriteLine($"Calling {invocation?.Method?.DeclaringType?.Name}.{invocation?.Method?.Name} : {methodReference}");

  var startNew = Stopwatch.StartNew();

  invocation.Proceed();
  invocation.ReturnValue = WatchAsync(methodReference, startNew, (Task)invocation.ReturnValue);
}

private static async Task WatchAsync(Guid methodReference,
    Stopwatch stopwatch, Task methodExecution)
{
  try
  {
    await methodExecution.ConfigureAwait(false);
  }
  finally
  {
    stopwatch.Stop();
    Console.WriteLine($"{methodReference} : Done, time taken: {stopwatch.ElapsedMilliseconds}ms");
  }
}

关于c# - Autofac拦截异步执行顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32994263/

相关文章:

node.js - 如何子类化 Model 并正确添加静态方法?

c# - 在 UI 线程上调用异步方法

c# - 在 Startup.cs 中使用 DI 解析服务

c# - 如何在 MVC4 网站 : do I really need to expose my dev environment? 中集成和测试 PayPal

c# - 如何在 C# 窗口应用程序中以编程方式创建按钮?

c# - 获取异步结果死锁(尽管将 configure await 设置为 false)

ioc-container - Autofac:解决对命名实例的特定依赖

c# - 注册 AutoMapper ValueResolvers?

c# - 如何使用 VS2017 Vstest.console.exe 运行 VS 2015 测试

c# - 如何使用 C# 跨域验证域用户凭据?