前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET Core 利用委托实现动态流程组装

.NET Core 利用委托实现动态流程组装

作者头像
郑子铭
发布2022-03-22 16:21:46
3410
发布2022-03-22 16:21:46
举报

前言

.NET Core 源码的管道模型中间件(Middleware)部分,觉得这个流程组装,思路挺好的,于是就分享给大家。

本次代码实现就直接我之前写的动态代理实现AOP的基础上就不另起炉灶了,主要思路就是运用委托。

对委托不理解的可留言,我写一篇委托的常规使用方式,以及底层原理(编译器)的文章。

接下来进入正题

1、定义接口IInterceptor

定义好我们AOP需要实现的接口,不同职责可以定义不同接口,大家根据实际情况划分

代码语言:javascript
复制
internal interface IInterceptor
{

}

internal interface IInterceptorAction : IInterceptor
{
    /// <summary>
    /// 执行之前
    /// </summary>
    /// <param name="args">参数</param>
    void AfterAction(object?[]? args);


    /// <summary>
    /// 执行之后
    /// </summary>
    /// <param name="args">参数</param>
    /// <param name="result">结果</param>
    void BeforeAction(object?[]? args, object? result);
}

2、定义特性

这里只定义一个基类特性类,继承标记接口,用于设置共通配置,且利于后面反射查找

代码语言:javascript
复制
[AttributeUsage(AttributeTargets.Class)]
internal class BaseInterceptAttribute : Attribute, IInterceptor
{

}

3、编写生成代理类的逻辑

只需要继承.NET CORE 原生DispatchProxy类,重写相关业务代码

3.1、编写创建代理方法

编写一个我们自己的Create方法(),这两个参数为了后面调用目标类储备的,方法实现就只需要调用DispatchProxy类的Create()

代码语言:javascript
复制
internal class DynamicProxy<T> : DispatchProxy
{
        public T Decorated { get; set; }//目标类
        public IEnumerable<IInterceptorAction> Interceptors { get; set; }  // AOP动作

        /// <summary>
        /// 创建代理实例
        /// </summary>
        /// <param name="decorated">代理的接口类型</param>
        /// <param name="afterAction">方法执行前执行的事件</param>
        /// <param name="beforeAction">方法执行后执行的事件</param>
        /// <returns></returns>
        public T Create(T decorated, IEnumerable<IInterceptor> interceptors)
        {
            object proxy = Create<T, DynamicProxy<T>>(); // 调用DispatchProxy 的Create  创建一个代理实例
            DynamicProxy<T> proxyDecorator = (DynamicProxy<T>)proxy;
            proxyDecorator.Decorated = decorated;
            proxyDecorator.Interceptors = interceptors.Where(c=>c.GetType().GetInterface(typeof(IInterceptorAction).Name) == typeof(IInterceptorAction)).Select(c=>c as IInterceptorAction);
            return (T)proxy;
        }
}
3.2、重写Invoke方法

这个就是需要实现我们自己的业务了,大家看注释应该就能看懂个大概了,目前这里只处理了IInterceptorAction接口逻辑,比如异常、异步等等,自己可按需实现。而_流程组装_的精髓就三步

1、不直接去执行targetMethod.Invoke(),而是把它放到委托里面。

2、定义AssembleAction()方法来组装流程,方法里面也不执行方法,也是返回一个执行方法的委托。

3、循环事先在Create()方法存储的特性实例,调用AssembleAction()方法组装流程,这样就达到俄罗斯套娃的效果了。

代码语言:javascript
复制
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
        Exception exception = null;//由委托捕获变量,用来存储异常
        Func<object?[]?, object?> action = (args) =>
        {
            try
            {
                return targetMethod?.Invoke(Decorated, args);
            }
            catch (Exception ex)//捕获异常,不影响AOP继续执行
            {
                exception = ex;
            }
            return null;
        };
        //进行倒序,使其按照由外置内的流程执行
        foreach (var c in Interceptors.Reverse())
        {
            action = AssembleAction(action, c);
        }
        //执行组装好的流程
        var result = action?.Invoke(args);
        //如果方法有异常抛出异常
        if (exception != null)
        {
            throw exception;
        }
        return result;
    }

    private Func<object?[]?, object?>? AssembleAction(Func<object?[]?, object?>? action, IInterceptorAction c)
    {
        return (args) =>
        {
            //执行之前的动作
            AfterAction(c.AfterAction, args);
            var result = action?.Invoke(args);
            //执行之后的动作
            BeforeAction(c.BeforeAction, args, result);
            return result;
        };
    }


    private void AfterAction(Action<object?[]?> action, object?[]? args)
    {
        try
        {
            action(args);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"执行之前异常:{ex.Message},{ex.StackTrace}");
        }
    }

    private void BeforeAction(Action<object?[]?, object?> action, object?[]? args, object? result)
    {
        try
        {
            action(args, result);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"执行之后异常:{ex.Message},{ex.StackTrace}");
        }
    }
}

4、定义一个工厂

工厂用于专门来为我们创建代理类,逻辑很简单,后续大家也可以按需编写,目前逻辑就是利用反射获取目标类的特性,把参数组装起来。

代码语言:javascript
复制
internal class ProxyFactory
{
    /// <summary>
    /// 创建代理实例
    /// </summary>
    /// <param name="decorated">代理的接口类型</param>
    /// <returns></returns>
    public static T Create<T>()
    {
        var decorated = ServiceHelp.GetService<T>();
        var type = decorated.GetType();
        var interceptAttribut = type.GetCustomAttributes<BaseInterceptAttribute>();
        //创建代理类
        var proxy = new DynamicProxy<T>().Create(decorated, interceptAttribut);
        return proxy;
    }
}

5、定义ServiceHelp

这个是为了使得我们全局只用一个作用域的IOC容器

代码语言:javascript
复制
public static class ServiceHelp
{

    public static IServiceProvider? serviceProvider { get; set; }

    public static void BuildServiceProvider(IServiceCollection serviceCollection)
    {
        //构建容器
        serviceProvider = serviceCollection.BuildServiceProvider();
    }

    public static T GetService<T>(Type serviceType)
    {
        return (T)serviceProvider.GetService(serviceType);
    }

    public static T GetService<T>()
    {
        return serviceProvider.GetService<T>();
    }
}

6、测试

6.1、编程AOP实现

写两个特性实现,继承基类特性,实现Action接口逻辑,测试两个特性随意调换位置进行组装流程

代码语言:javascript
复制
internal class AOPTest1Attribut : BaseInterceptAttribute, IInterceptorAction
{
    public void AfterAction(object?[]? args)
    {
        Console.WriteLine($"AOP1方法执行之前,args:{args[0] + "," + args[1]}");
        // throw new Exception("异常测试(异常,但依然不能影响程序执行)");
    }

    public void BeforeAction(object?[]? args, object? result)
    {
        Console.WriteLine($"AOP1方法执行之后,result:{result}");
    }
}

internal class AOPTest2Attribut : BaseInterceptAttribute, IInterceptorAction
{
    public void AfterAction(object?[]? args)
    {
        Console.WriteLine($"AOP2方法执行之前,args:{args[0] + "," + args[1]}");
    }

    public void BeforeAction(object?[]? args, object? result)
    {
        Console.WriteLine($"AOP2方法执行之后,result:{result}");
    }
}

6.2、编写测试服务

写一个简单的测试服务,就比如两个整数相加,然后标记上我们写的AOP特性

代码语言:javascript
复制
internal interface ITestService
{
    public int Add(int a, int b);
}

[AOPTest2Attribut]
[AOPTest1Attribut]
internal class TestService : ITestService
{
    public int Add(int a, int b)
    {
        Console.WriteLine($"正在执行--》Add({a},{b})");
        //throw new Exception("方法执行--》测试异常");
        return a + b;
    }
}
6.3、调用

1、把服务注册到IOC2.调用创建代理类的工厂3.调用测试服务函数:.Add(1, 2)

代码语言:javascript
复制
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ITestService, TestService>();
ServiceHelp.BuildServiceProvider(serviceCollection);
//用工厂获取代理实例
var s = ProxyFactory.Create<ITestService>();
var sum = s.Add(1, 2);
6.4 效果图

AOP1->AOP2->Add(a,b)

AOP2->AOP1->Add(a,b)

代码上传至gitee,AOP流程组装分支

https://gitee.com/luoxiangbao/dynamic-proxy.git

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2、定义特性
  • 3、编写生成代理类的逻辑
    • 3.1、编写创建代理方法
      • 3.2、重写Invoke方法
      • 4、定义一个工厂
      • 5、定义ServiceHelp
      • 6、测试
        • 6.1、编程AOP实现
          • 6.3、调用
            • 6.4 效果图
            相关产品与服务
            测试服务
            测试服务 WeTest 包括标准兼容测试、专家兼容测试、手游安全测试、远程调试等多款产品,服务于海量腾讯精品游戏,涵盖兼容测试、压力测试、性能测试、安全测试、远程调试等多个方向,立体化安全防护体系,保卫您的信息安全。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档