2015-10-07 6 views
1

Так у меня есть этот класс:Autofac Перехват Порядок выполнения асинхронной

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 Interceptor:

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"); 
    } 
} 

Это производит вывод:

Вызов IMapperBootstrap.Map: 54425559 -71fe-4f23-ab47-d0f3371ec819
Вызов IMapper1.Map: 51babb34-fa83-42ed-84e7-a1e979528116
51babb34-fa83-42ed-84e7-a1e979528116: Готово, время, затрачиваемое: 219ms
54425559-71fe-4f23-ab47-d0f3371ec819: Готово, время, затрачиваемое: 221ms
Вызов IMapper2.Map: 41c812a2-d82d-48f6-9b8d -139b52eb28e3
41c812a2-d82d-48f6-9b8d-139b52eb28e3: Готово, время, затрачиваемое: 9ms
Calling IMapper3.Map: c91bed04-8f86-47d3-a35a-417e354c2c5f
c91bed04-8f86-47d3-a35a-417e354c2c5f: Готово, время съемки: 994ms
Вызов IMapper4.Map: 035cad27-1ba8-4bd1-b85f-396f64998d97
035cad27-1ba8-4bd1-b85f-396f64998d97: Готово, время, затрачиваемое: 18мс

Как вы можете видеть, MappingBoostrap.Map заканчивается после первого Mapper1.Map, а не того, что я ожидаю, когда все функции завершены. Почему?


Autofac Config:

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

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

ответ

4

Почему?

При вызове асинхронного метода, как это:

mapping.Map(); 

это только начинается метод. Это how asynchronous methods work (как я расскажу в своем блоге). Если вы await задачу возвращенных асинхронного метода, то текущий метод остановится, пока асинхронный метод не завершается:

await mapping.Map(); 

В случае перехвата, то идеального решения будет иметь Proceed и Intercept метода асинхронная:

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

К сожалению, Autofac не имеет встроенный в понимании асинхронных методов, так что это не возможно. Вместо этого вам нужно позвонить Proceed, который только запускает асинхронный метод. Асинхронный метод возвращает Task, который представляет выполнение этого метода. Чтобы подключиться к завершению метода, вы должны заменить на Task с одним из своих.

Для простого Task -returning метод, вы можете использовать что-то вроде этого:

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"); 
    } 
} 
+0

Я задавался вопросом, было ли, что причина, но какая-то часть меня говорит, почему бы Autofac не поддерживает 'ждать/async' , но это похоже на простое решение. Дело в том, что вы хотите иметь обработчик перехвата, как вы ожидаете 'Task' или код, в котором происходит фактический вызов. Я думаю, это одна из тех вещей, которые вы должны учитывать при перехвате асинхронного кода. –

+0

@CallumLinington: Чтобы быть ясным, перехваченный код должен по-прежнему использовать 'await'. Код перехвата просто перехватывает возвращенную «Задачу» и заменяет ее одной из своих (которая, как представляется, выполняется с помощью 'async' /' await'). –

+0

Ahh да, конечно, спасибо –

Смежные вопросы