背景
autofac使用拦截器实现AOP,是基于Castle.Core的.然而Castle.Core并未提供原生异步支持.所以需要使用帮助类实现,这在autofac官方文档的已知问题中有详细说明。 对于该问题的讨论,最早出现于stackoverflow James Skimming基于其中的一个答案,研发了一个帮助包即: Castle.Core.AsyncInterceptor 我之前也一直使用的是该方案,不过thepirat000随后提出了一个使用dynamic的更加简化的实现方法 我对该方法进行了一些封装,实现了一个帮助包,大家可以尝试一下。
项目地址: https://github.com/wswind/lightwind
使用
使用时,你可以通过nuget安装Lightwind.Asyncinterceptor
也可以直接拷贝AsyncInterceptorBase.cs
放入你的项目中。样例代码可点击这里查看
其核心代码是封装实现一个支持异步处理的Interceptor
父类,让开发者能够继承实现自己的拦截器。开发时,仅需关注所拦截的方法,在执行前后该添加什么处理逻辑.
异步拦截器的执行流程如下:
在所拦截的方法执行前,首先执行BeforeProceed
,方法执行后,如果为同步方法,则后续执行AfterProceedSync
如果为异步方法,则await
所拦截的方法使其真正执行后,调用AfterProceedAsync
进行异步方法的后续处理。
对于异步执行且有返回值的情况,可通过hasAsynResult
参数判断是否有返回值,通过ProceedAsynResult
属性读取或修改所拦截方法的最终返回值.
代码的运行流程图如下:
AsyncInterceptorBase.cs
实现如下:
// Licence: MIT
// Author: Vincent Wang
// Email: ws_dev@163.com
// ProjectUrl: https://github.com/wswind/lightwind
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Castle.DynamicProxy;
namespace Lightwind.AsyncInterceptor
{
//inspired by : https://stackoverflow.com/a/39784559/7726468
public abstract class AsyncInterceptorBase : IInterceptor
{
public AsyncInterceptorBase()
{
}
public void Intercept(IInvocation invocation)
{
BeforeProceed(invocation);
invocation.Proceed();
if (IsAsyncMethod(invocation.MethodInvocationTarget))
{
// 关键实现语句
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue, invocation);
}
else
{
AfterProceedSync(invocation);
}
}
private bool CheckMethodReturnTypeIsTaskType(MethodInfo method)
{
var methodReturnType = method.ReturnType;
if(methodReturnType.IsGenericType)
{
if (methodReturnType.GetGenericTypeDefinition() == typeof(Task<>) ||
methodReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>))
return true;
}
else
{
if (methodReturnType == typeof(Task) ||
methodReturnType == typeof(ValueTask))
return true;
}
return false;
}
private bool IsAsyncMethod(MethodInfo method)
{
bool isDefAsync = Attribute.IsDefined(method, typeof(AsyncStateMachineAttribute), false);
bool isTaskType = CheckMethodReturnTypeIsTaskType(method);
bool isAsync = isDefAsync && isTaskType;
return isAsync;
}
protected object ProceedAsyncResult { get; set; }
private async Task InterceptAsync(Task task, IInvocation invocation)
{
await task.ConfigureAwait(false);
await AfterProceedAsync(invocation, false);
}
private async Task<TResult> InterceptAsync<TResult>(Task<TResult> task, IInvocation invocation)
{
ProceedAsyncResult = await task.ConfigureAwait(false);
await AfterProceedAsync(invocation, true);
return (TResult)ProceedAsyncResult;
}
private async ValueTask InterceptAsync(ValueTask task, IInvocation invocation)
{
await task.ConfigureAwait(false);
await AfterProceedAsync(invocation, false);
}
private async ValueTask<TResult> InterceptAsync<TResult>(ValueTask<TResult> task, IInvocation invocation)
{
ProceedAsyncResult = await task.ConfigureAwait(false);
await AfterProceedAsync(invocation, true);
return (TResult)ProceedAsyncResult;
}
protected virtual void BeforeProceed(IInvocation invocation) { }
protected virtual void AfterProceedSync(IInvocation invocation) { }
protected virtual Task AfterProceedAsync(IInvocation invocation, bool hasAsynResult)
{
return Task.CompletedTask;
}
}
}
上述代码的关键实现语句为InterceptAsync((dynamic)invocation.ReturnValue, invocation)
,C#会在运行时依据invocation.ReturnValue
的类型调用正确的重载函数,即:InterceptAsync
/ InterceptAsync<TResult>
对于异步拦截器的其他实现方法,可参考此代码样例。