Castle DynamicProxy异步(async/await)的支持 支持Task和ValueTask的异步拦截框架(AOP)

本文介绍了如何使用Castle.Core AsyncInterceptor 对.NET Core 3.1的异步方法进行拦截,并结合Autofac和UtilsSharp AsyncInterceptor,实现异常处理和日志记录,同时演示了如何集成Autofac以方便服务注册和依赖注入。

目录

一、前言

二、首先先nuget安装UtilsSharp.AsyncInterceptor

三、异步拦截类

四、拦截器

五、异常拦截信息处理

六、Autofac的集成

七、.Net Core 3.1上使用


一、前言

我们都知道Castle动态代理是对于同步方法的动态代理,那么我们介绍下如何使用Castle来对异步方法进行代理。

如果你站在应用程序的角度来看,异步只是微软的一个语法糖,使用异步的方法返回结果为一个Task/ValueTask或Task/ValueTask的对象,这对于DP来说和一个int类型并无差别,但是如果我们想要在拦截中获取到真实的返回结果,就需要添加一些额外的处理。

查了些资料,都是推荐我们使用这个异步拦截框架Castle.Core.AsyncInterceptor,但是这个库有个缺点就是仅支持Task,Task<TResult>,却不支持ValueTask,ValueTask<TResult> 。后面基于Castle.Core.AsyncInterceptor封装了支持ValueTask,ValueTask<TResult>的异步拦截框架:UtilsSharp.AsyncInterceptor

二、首先先nuget安装UtilsSharp.AsyncInterceptor

三、异步拦截类

异步拦截类主要是处理同步方法和异步方法的拦截,可以支持拦截 void、TResult、Task、Task<TResult>、ValueTask、ValueTask<TResult>的方法

/// <summary>
/// 日志异步拦截
/// </summary>
public class LoggerAsyncInterceptor : IAsyncInterceptor
{
    /// <summary>
    /// Exception 匹配规则
    /// </summary>
    public virtual List<ExceptionRegexRule> ExceptionRegexRule { set; get; } = BaseException.GetDefaultRegexRule();

    #region 同步方法拦截时使用
    /// <summary>
    /// 同步方法拦截时使用
    /// </summary>
    /// <param name="invocation"></param>
    public virtual void InterceptSynchronous(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        catch (Exception ex)
        {
            InterceptorException.Interceptor(invocation, ex, ExceptionRegexRule);
        }
    }
    #endregion

    #region 异步方法返回Task拦截时使用

    /// <summary>
    /// 异步方法返回Task拦截时使用
    /// </summary>
    /// <param name="invocation">IInvocation</param>
    public virtual void InterceptTaskAsynchronous(IInvocation invocation)
    {
        //调用业务方法
        invocation.ReturnValue = InternalInterceptTaskAsynchronous(invocation);
    }

    /// <summary>
    /// 异步方法返回Task`1拦截时使用 私有方法
    /// </summary>
    /// <param name="invocation">IInvocation</param>
    /// <returns></returns>
    protected virtual async Task InternalInterceptTaskAsynchronous(IInvocation invocation)
    {
        try
        {
            //获取执行信息
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            await task;
            //记录日志
        }
        catch (Exception ex)
        {
            InterceptorException.Interceptor(invocation, ex, ExceptionRegexRule);
        }
    }

    #endregion

    #region 异步方法返回Task<TResult>拦截时使用

    /// <summary>
    /// 异步方法返回Task`1拦截时使用
    /// </summary>
    /// <typeparam name="TResult">返回参数</typeparam>
    /// <param name="invocation">IInvocation</param>
    public virtual void InterceptTaskAsynchronous<TResult>(IInvocation invocation)
    {
        //调用业务方法
        invocation.ReturnValue = InternalInterceptTaskAsynchronous<TResult>(invocation);
    }

    /// <summary>
    /// 异步方法返回Task`1拦截时使用 私有方法
    /// </summary>
    /// <typeparam name="TResult">返回参数</typeparam>
    /// <param name="invocation">IInvocation</param>
    /// <returns></returns>
    protected virtual async Task<TResult> InternalInterceptTaskAsynchronous<TResult>(IInvocation invocation)
    {
        try
        {
            //获取执行信息
            invocation.Proceed();
            var task = (Task<TResult>)invocation.ReturnValue;
            TResult result = await task;
            //记录日志
            return result;
        }
        catch (Exception ex)
        {
            return InterceptorException.InterceptorAsync<TResult>(invocation, ex, ExceptionRegexRule);
        }
    }

    #endregion

    #region 异步方法返回ValueTask拦截时使用

    /// <summary>
    /// 异步方法返回ValueTask拦截时使用
    /// </summary>
    /// <param name="invocation">IInvocation</param>
    public virtual void InterceptValueTaskAsynchronous(IInvocation invocation)
    {
        //调用业务方法
        invocation.ReturnValue = InternalInterceptValueTaskAsynchronous(invocation);
    }

    /// <summary>
    /// 异步方法返回ValueTask拦截时使用 私有方法
    /// </summary>
    /// <param name="invocation">IInvocation</param>
    /// <returns></returns>
    protected virtual async ValueTask InternalInterceptValueTaskAsynchronous(IInvocation invocation)
    {
        try
        {
            //获取执行信息
            invocation.Proceed();
            var task = (ValueTask)invocation.ReturnValue;
            await task;
            //记录日志
        }
        catch (Exception ex)
        {
            InterceptorException.Interceptor(invocation, ex, ExceptionRegexRule);
        }
    }

    #endregion

    #region 异步方法返回ValueTask<TResult>拦截时使用

    /// <summary>
    /// 异步方法返回ValueTask`1拦截时使用
    /// </summary>
    /// <typeparam name="TResult">返回参数</typeparam>
    /// <param name="invocation">IInvocation</param>
    public virtual void InterceptValueTaskAsynchronous<TResult>(IInvocation invocation)
    {
        //调用业务方法
        invocation.ReturnValue = InternalInterceptValueTaskAsynchronous<TResult>(invocation);
    }

    /// <summary>
    /// 异步方法返回ValueTask`1拦截时使用 私有方法
    /// </summary>
    /// <typeparam name="TResult">返回参数</typeparam>
    /// <param name="invocation">IInvocation</param>
    /// <returns></returns>
    protected virtual async ValueTask<TResult> InternalInterceptValueTaskAsynchronous<TResult>(IInvocation invocation)
    {
        try
        {
            //获取执行信息
            invocation.Proceed();
            var task = (ValueTask<TResult>)invocation.ReturnValue;
            TResult result = await task;
            //记录日志
            return result;
        }
        catch (Exception ex)
        {
            return InterceptorException.InterceptorAsync<TResult>(invocation, ex, ExceptionRegexRule);
        }

    }

    #endregion

}

四、拦截器

然后,定义拦截器,实现IInterceptor接口

 /// <summary>
 /// 异步拦截
 /// </summary>
 /// <typeparam name="TAsyncInterceptor">异步拦截服务</typeparam>
 public class AsyncInterceptor<TAsyncInterceptor> : IInterceptor where TAsyncInterceptor : IAsyncInterceptor
 {

     private readonly TAsyncInterceptor _t;

     /// <summary>
     /// 异步拦截
     /// </summary>
     public AsyncInterceptor(TAsyncInterceptor t)
     {
         _t = t;
     }

     /// <summary>
     /// 拦截主方法
     /// </summary>
     /// <param name="invocation"></param>
     public void Intercept(IInvocation invocation)
     {
         _t.ToInterceptor().Intercept(invocation);
     }

 }

五、异常拦截信息处理

这个类主要是实现对try{}catch{}异常捕捉后的消息进行日志记录和返回参赋值。返回参如果是BaseResult<T>、Task<BaseResult<T>>、ValueTask<BaseResult<T>>类型的,会把错误信息自动赋值上去。本类依赖UtilSharp 3.4.0和UtilSharp.Logger 2.0.0

 /// <summary>
 /// 拦截异常信息
 /// </summary>
 public class InterceptorException
 {
     #region 拦截异常信息
     /// <summary>
     /// 拦截异常信息
     /// </summary>
     /// <param name="invocation">IInvocation</param>
     /// <param name="ex">Exception</param>
     /// <param name="rules">Exception 拦截规则</param>
     public static void Interceptor(IInvocation invocation, Exception ex, List<ExceptionRegexRule> rules)
     {
         var result = Result(invocation, ex, rules);
         invocation.ReturnValue = result;
     }


     /// <summary>
     /// 拦截异常信息(异步带参数)
     /// </summary>
     /// <typeparam name="TResult">TResult</typeparam>
     /// <param name="invocation">IInvocation</param>
     /// <param name="ex">Exception</param>
     /// <param name="rules">Exception 拦截规则</param>
     /// <returns></returns>
     public static TResult InterceptorAsync<TResult>(IInvocation invocation, Exception ex, List<ExceptionRegexRule> rules)
     {
         var result = Result(invocation, ex, rules);
         return (TResult)result;
     }

     /// <summary>
     /// 获取错误信息
     /// </summary>
     /// <param name="invocation">IInvocation</param>
     /// <param name="ex">Exception</param>
     /// <param name="rules">Exception 拦截规则</param>
     /// <returns></returns>
     private static object Result(IInvocation invocation, Exception ex, List<ExceptionRegexRule> rules)
     {
         // 判断方法是否为异步方法
         MethodInfo methodInfo = invocation.Method;
         bool isAsyncMethod = IsAsyncMethod(methodInfo);
         //同步方法
         var returnType = invocation.Method.ReturnType;
         if (isAsyncMethod)
         {   //异步方法
             returnType = invocation.Method.ReturnType.GetGenericArguments().First();
         }
         var setErrorMethod = returnType.GetMethod("SetError", new Type[] { typeof(string), typeof(int) });
         var result = returnType != typeof(string) ? Activator.CreateInstance(returnType, true) : "";
         var methodName = invocation.InvocationTarget + "." + invocation.Method.Name;
         var args = new List<string>();
         if (invocation.Arguments != null && invocation.Arguments.Length > 0)
         {
             args.AddRange(from item in invocation.Arguments where item != null select JsonConvert.SerializeObject(item));
         }
         var param = string.Join(",", args);
         if (setErrorMethod == null)
         {
             LogHelper.Error($"执行:{methodName} 异常", ex, parameters: $"{param}", func: $"{methodName}");
         }
         else
         {
             var logId = LogHelper.Error($"{methodName} 异常", ex, parameters: $"{param}", func: $"{methodName}");
             var r = ex.Regex(logId, rules);
             setErrorMethod.Invoke(result, new object[] { r.Msg, r.Code });
         }
         return result;
     }

     /// <summary>
     /// 判断是否是异步方法
     /// </summary>
     /// <param name="methodInfo">方法信息</param>
     /// <returns></returns>
     private static bool IsAsyncMethod(MethodInfo methodInfo)
     {
         return typeof(System.Threading.Tasks.Task).IsAssignableFrom(methodInfo.ReturnType) ||
                (methodInfo.ReturnType.IsGenericType &&
                 methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(System.Threading.Tasks.Task<>));
     }


     #endregion
 }

六、Autofac的集成

Autofac集成了对DynamicProxy的支持,我们需要引用Autofac.Extras.DynamicProxy,然后创建容器、注册服务、生成实例、调用方法,我们来看下面的代码:

static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();
    //注册拦截器
    builder.RegisterType<AsyncInterceptor<LoggerAsyncInterceptor>>().AsSelf();
    builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();
    //注册要拦截的服务
    builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
        .EnableInterfaceInterceptors()                  //启用接口拦截
        .InterceptedBy(typeof(AsyncInterceptor<LoggerAsyncInterceptor>));      //指定拦截器
    var container = builder.Build();
    IProductRepository productRepo = container.Resolve<IProductRepository>();
    productRepo.Get();
}

七、.Net Core 3.1上使用

前面是跟大家展示详细的实现过程,如果大家是.Net Core 3.1 项目,不用去写上面那些代码,可以nuget直接安装UtilsSharp.AspNetCore框架(2.1.1以上版本),UtilsSharp.AspNetCore 3.4.0已经集成了UtilsSharp.AsyncInterceptor和Autofac.Extras.DynamicProxy框架,只要在Startup.cs文件这么简单配置即可

public class Startup : AutofacStartup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //初始化Protobuf模型
            //ProtobufRunTime.Initialize();
            // 验证入参模型
            //services.AddValidationExtensions();
            //ElasticSearch配置
            //ElasticSearchConfig.ElasticSearchSetting = JsonConvert.DeserializeObject<ElasticSearchSetting>(BaseConfig.EsSettingJson);
            //MQ队列配置
            //RabbitMqConfig.RabbitMqSetting = new RabbitMqSetting { RabbitMqConnection =BaseConfig.RabbitMqConnection};
            //RabbitMqManager.Register();
            //swagger配置
            AspNetCoreExtensionsConfig.SwaggerDocOptions = new SwaggerDocOptions
            {
                Enable = true,
                ProjectName = "日志管理中心",
                ProjectDescription = "日志管理中心接口",
                Groups = new List<SwaggerGroup>
                {
                    new SwaggerGroup() {GroupName = "log", Title = "日志记录接口", Version = "v1.0"}
                }
            };
            //ThreadPool.SetMinThreads(500, 400);
            services.AddAspNetCoreExtensions();
            services.AddControllers().AddNewtonsoftJson();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //注册扩展
            app.UseAspNetCoreExtensions();
            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                //配置控制器映射
                endpoints.MapControllers();
            });
        }

        /// <summary>
        /// 依赖注入映射
        /// </summary>
        /// <param name="builder"></param>
        public override void ConfigureContainer(ContainerBuilder builder)
        {
            Init<AsyncInterceptor<LoggerAsyncInterceptor>>(builder);
        }
    }

主要关键代码是最后这个配置下,大家还可以按自己的拦截需求重写LoggerAsyncInterceptor这个类

        /// <summary>
        /// 依赖注入映射
        /// </summary>
        /// <param name="builder"></param>
        public override void ConfigureContainer(ContainerBuilder builder)
        {
            Init<AsyncInterceptor<LoggerAsyncInterceptor>>(builder);
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值