菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)的方法

 更新时间:2024年06月20日 10:46:29   作者:痴者工良  
CZGL.AOP 是 基于 EMIT 编写的 一个简单轻量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多种依赖注入框架,本文介绍菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)的相关知识,感兴趣的朋友一起看看吧

Nuget 库地址:https://www.nuget.org/packages/CZGL.AOP/

Github 库地址:https://github.com/whuanle/CZGL.AOP

CZGL.AOP 是 基于 EMIT 编写的 一个简单轻量的AOP框架,支持非侵入式代理,支持.NET Core/ASP.NET Core,以及支持多种依赖注入框架。

1,快速入门

CZGL.AOP 使用比较简单,你只需要使用 [Interceptor] 特性标记需要代理的类型,然后使用继承 ActionAttribute 的特性标记要被代理的方法或属性。

1.1 继承 ActionAttribute 特性

ActionAttribute 是用于代理方法或属性的特性标记,不能直接使用,需要继承后重写方法。

示例如下:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

Before 会在被代理的方法执行前或被代理的属性调用时生效,你可以通过 AspectContext 上下文,获取、修改传递的参数。

After 在方法执行后或属性调用时生效,你可以通过上下文获取、修改返回值。

1.2 标记代理类型

在被代理的类型中,使用 [Interceptor] 特性来标记,在需要代理的方法中,使用 继承了 ActionAttribute 的特性来标记。

此方法是侵入式的,需要在编译前完成。

[Interceptor]
public class Test : ITest
{
    [Log] public virtual string A { get; set; }
    [Log]
    public virtual void MyMethod()
    {
        Console.WriteLine("运行中");
    }
}

注意的是,一个方法或属性只能设置一个拦截器。

2,如何创建代理类型

CZGL.AOP 有多种生成代理类型的方式,下面介绍简单的方式。

请预先创建如下代码:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
    public interface ITest
    {
        void MyMethod();
    }
    [Interceptor]
    public class Test : ITest
    {
        [Log] public virtual string A { get; set; }
        public Test()
        {
            Console.WriteLine("构造函数没问题");
        }
        [Log]
        public virtual void MyMethod()
        {
            Console.WriteLine("运行中");
        }
    }

2.1 通过API直接创建

通过 CZGL.AOP 中的 AopInterceptor 类,你可以生成代理类型。

示例如下:

            ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
            Test test2 = AopInterceptor.CreateProxyOfClass<Test>();
            test1.MyMethod();
            test2.MyMethod();

CreateProxyOfInterface 通过接口创建代理类型;CreateProxyOfClass 通过类创建代理类型;

默认调用的是无参构造函数。

3,创建代理类型

通过API

你可以参考源码解决方案

中的 ExampleConsole 项目。

如果要直接使用 AopInterceptor.CreateProxyOfInterface 和 AopInterceptor.CreateProxyOfClass 方法,通过接口或类来创建代理类型。

        ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>();
        Test test2 = AopInterceptor.CreateProxyOfClass<Test>();

如果要指定实例化的构造函数,可以这样:

            // 指定构造函数
            test2 = AopInterceptor.CreateProxyOfClass<Test>("aaa", "bbb");
            test2.MyMethod();

通过 Microsoft.Extensions.DependencyInjection

Microsoft.Extensions.DependencyInjection 是 .NET Core/ASP.NET Core 默认的依赖注入容器。

如果需要支持 ASP.NET Core 中使用 AOP,你可以在 Nuget 包中安装 CZGL.AOP.MEDI

如果你在控制台下使用 Microsoft.Extensions.DependencyInjection,你可以使用名为 BuildAopProxy 的 IServiceCollection 拓展方法来为容器中的类型,生成代理类型。

示例如下:

            IServiceCollection _services = new ServiceCollection();
            _services.AddTransient<ITest, Test>();
            var serviceProvider = _services.BuildAopProxy().BuildServiceProvider();
            serviceProvider.GetService<ITest>();
            return serviceProvider;

你可以参考源码解决方案中的 ExampleMEDI 项目。

如果你要在 ASP.NET Core 中使用,你可以在 Startup 中,ConfigureServices 方法的最后一行代码使用 services.BuildAopProxy(); 。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.BuildAopProxy();
        }

还可以在 Program 的 IHostBuilder 中使用 .UseServiceProviderFactory(new AOPServiceProxviderFactory()) 来配置使用 CZGL.AOP。

示例:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AOPServiceProxviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

可以参考解决方案中的 ExampleConsole 和 ExampleWebMEDI 两个项目。

你不必担心引入 CZGL.AOP 后,使用依赖注入会使程序变慢或者破坏容器中的原有属性。CZGL.AOP 只会在创建容器时处理需要被代理的类型,不会影响容器中的服务,也不会干扰到依赖注入的执行。

通过 Autofac

如果需要在 Autofac 中使用 AOP,则需要引用 CZGL.AOP.Autofac 包。

如果你在控制台程序中使用 Autofac,则可以在 Build() 后面使用 BuildAopProxy()

            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<Test>().As<ITest>();
            var container = builder.Build().BuildAopProxy();
            using (ILifetimeScope scope = container.BeginLifetimeScope())
            {
                // 获取实例
                ITest myService = scope.Resolve<ITest>();
                myService.MyMethod();
            }
            Console.ReadKey();
        }

要注意的是,在已经完成的组件注册创建一个新的容器后,才能调用 BuildAopProxy() 方法,

这样针对一个新的容器你可以考虑是否需要对容器中的组件进行代理。

如果在 ASP.NET Core 中使用 Autofac,你需要在 Program 类的 IHostBuilder 中使用:

.UseServiceProviderFactory(new AutofacServiceProviderFactory())

如果需要代理已经注册的组件,则将其替换为:

 .UseServiceProviderFactory(new CZGL.AOP.Autofac.AOPServiceProxviderFactory())

请参考 源码解决方案中的 ExampleAutofac 和 ExampleWebAutofac 两个项目。

4,深入使用

代理类型

要被代理的类型,需要使用 [Interceptor]来标记,例如:

    [Interceptor]
    public class Test : ITest
    {
    }

支持泛型类型。

被代理的类型必须是可被继承的。

类型的构造函数没有限制,你可以随意编写。

在使用 API 创建代理类型并且实例化时,你可以指定使用哪个构造函数。

例如:

string a="",b="",c="";
			ITest test1 = AopInterceptor.CreateProxyOfInterface<ITest, Test>(a,b,c);

API 会根据参数的多少以及参数的类型自动寻找合适的构造函数。

方法、属性代理

为了代理方法或属性,你需要继承 ActionAttribute 特性,然后为方法或属性标记此特性,并且将方法或属性设置为 virtual

一个类型中的不同方法,可以使用不同的拦截器。

        [Log1]
        public virtual void MyMethod1(){}
        [Log2]
        public virtual void MyMethod2(){}

对于属性,可以在属性上直接使用特性,或者只在 get 或 set 构造器使用。

        [Log] public virtual string A { get; set; }
        // 或
        public virtual string A { [Log] get; set; }
        // 或
        public virtual string A { get; [Log] set; }

如果在属性上使用特性,相当于 [Log] get; [Log] set;

上下文

一个简单的方法或属性拦截标记是这样的:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }

AspectContext 的属性说明如下:

字段说明
Type当前被代理类型生成的代理类型
ConstructorParamters类型被实例化时使用的构造函数的参数,如果构造函数没有参数,则 MethodValues.Length = 0,而不是 MethodValues 为 null。
IsProperty当前拦截的是属性
PropertyInfo当前被执行的属性的信息,可为 null。
PropertyValue但调用的是属性时,返回 get 的结果或 set 的 value 值。
IsMethod当前拦截的是方法
MethodInfo当前方法的信息
MethodValues方法被调用时传递的参数,如果此方法没有参数,则 MethodValues.Length = 0,而不是 MethodValues 为 null
MethodResult方法执行返回的结果(如果有)

拦截方法或属性的参数

通过上下文,你可以修改方法或属性的参数以及拦截返回结果:

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            // 拦截并修改方法的参数
            for (int i = 0; i < context.MethodValues.Length; i++)
            {
                context.MethodValues[i] = (int)context.MethodValues[i] + 1;
            }
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            // 拦截方法的执行结果
            context.MethodResult = (int)context.MethodResult + 664;
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
    [Interceptor]
    public class Test
    {
        [Log]
        public virtual int Sum(int a, int b)
        {
            Console.WriteLine("运行中");
            return a + b;
        }
    }
            Test test = AopInterceptor.CreateProxyOfClass<Test>();
            Console.WriteLine(test.Sum(1, 1));

方法的参数支持 inrefout;支持泛型方法泛型属性;支持异步方法;

非侵入式代理

此种方式不需要改动被代理的类型,你也可以代理程序集中的类型。

    public class LogAttribute : ActionAttribute
    {
        public override void Before(AspectContext context)
        {
            Console.WriteLine("执行前");
        }
        public override object After(AspectContext context)
        {
            Console.WriteLine("执行后");
            if (context.IsMethod)
                return context.MethodResult;
            else if (context.IsProperty)
                return context.PropertyValue;
            return null;
        }
    }
    public class TestNo
    {
        public virtual string A { get; set; }
        public virtual void MyMethod()
        {
            Console.WriteLine("运行中");
        }
    }
            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(new ProxyTypeBuilder()
                .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod(typeof(LogAttribute), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));

通过 ProxyTypeBuilder 来构建代理类型。

代理方法或属性都是使用 AddProxyMethod,第一个参数是要使用的拦截器,第二个参数是要拦截的方法。

如果要拦截属性,请分开设置属性的 getset 构造。

如果多个方法或属性使用同一个拦截器,则可以这样:

            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));
            TestNo test3 = AopInterceptor.CreateProxyOfType<TestNo>(
                new ProxyTypeBuilder(new Type[] { typeof(LogAttribute) })
                .AddProxyMethod("LogAttribute", typeof(TestNo).GetMethod(nameof(TestNo.MyMethod)))
                .AddProxyMethod(typeof(LogAttribute2), typeof(TestNo).GetProperty(nameof(TestNo.A)).GetSetMethod()));

在构造函数中传递过去所需要的拦截器,然后在拦截时使用。

到此这篇关于菜渣开源一个基于 EMIT 的 AOP 库(.NET Core)的文章就介绍到这了,更多相关EMIT 的 AOP 库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具(推荐)

    使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具(推荐)

    这篇文章主要介绍了使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-05-05
  • 利用EF6简单实现多租户的应用

    利用EF6简单实现多租户的应用

    这篇文章主要给大家介绍了关于如何利用EF6简单实现多租户应用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用EF6具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • 在ASP.NET里得到网站的域名

    在ASP.NET里得到网站的域名

    在ASP.NET里得到网站的域名...
    2006-09-09
  • Ubuntu16.04系统配置.net core环境

    Ubuntu16.04系统配置.net core环境

    本文给大家介绍的是在Ubuntu16.04系统配置.net core环境的方法,由于作者本身用的是WIN系统,所以这次教程是在VMware中进行的,希望大家能够喜欢。
    2016-07-07
  • .Net的GC垃圾回收原理及实现

    .Net的GC垃圾回收原理及实现

    在.Net应用程序中很多问题都是没有正确的理解垃圾回收的工作原理而导致的,本文就介绍一下.Net的GC垃圾回收原理及实现,感兴趣的小伙伴们可以参考一下
    2021-05-05
  • .NET Core GC压缩(compact_phase)底层原理解析

    .NET Core GC压缩(compact_phase)底层原理解析

    GC的最后一个步骤,分为清除和压缩两种情况,清除操作将不可到达对象转换为Free,而压缩操作涉及复制对象并移动到新位置,更新所有引用,并重新划分代边界,本文介绍.NET Core GC压缩原理解析,感兴趣的朋友一起看看吧
    2025-01-01
  • asp.net中获取新增加记录的ID Access版

    asp.net中获取新增加记录的ID Access版

    在实际开发中有时需要获取新增加的记录的ID。如以新增加ID为文件名生成静态页等
    2012-03-03
  • 理解ASP.NET Core 启动类(Startup)

    理解ASP.NET Core 启动类(Startup)

    这篇文章主要介绍了ASP.NET Core 启动类(Startup),文中运用代码讲解相关知识非常详细,感兴趣的小伙伴可以参考一下
    2021-09-09
  • .Net Core 2.2升级3.1的避坑指南(小结)

    .Net Core 2.2升级3.1的避坑指南(小结)

    这篇文章主要介绍了.Net Core 2.2升级3.1的避坑指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 在.NET 6.0中自定义接口路由的方法

    在.NET 6.0中自定义接口路由的方法

    这篇文章主要介绍了在.NET 6.0中自定义接口路由,在本文,我们学习了如何使用终止中间件组件作为接口,并用将该接口映射到新的路由引擎,从而让我们的路由变得更加强大和灵活,需要的朋友可以参考下
    2023-04-04

最新评论