增量生成器(Incremental Generator)实现AOP代理

本文介绍使用增量生成器生成AOP代理类

nuget包安装

dotnet add package AutoAopProxyGenerator --version 0.0.4

该nuget包主要是为一些类生成对应的代理类,以便实现一些aop操作,例如异常处理、日志记录等等。

该nuget包提供了3个attribute进行配置,以及一个接口

AddAspectHandlerAttribute只适用于接口/接口上的方法,顾名思义,添加AOP处理类

GenAspectProxyAttribute只适用于类,告诉生成器,需要为他生成代理类

IgnoreAspectAttribute只适用于接口中的方法,不生成代理方法

IAspectHandlerAOP处理类需要实现的接口

接下来依然是使用一些简单的伪代码进行说明

声明一个接口,并且配置两个处理类

[AddAspectHandler(AspectType = typeof(ExceptionAop))]
[AddAspectHandler(AspectType = typeof(TestAop))]
public interface IHelloService
{
    Task<string> SayHelloAsync(string name);
    [IgnoreAspect]
    string SayHelloDirectly(string name);
}

实现该接口,并标注GenAspectProxyAttribute

[GenAspectProxy]
public class HelloService : IHelloService
{
    public async Task<string> SayHelloAsync(string name)
    {
        if (name.IndexOf("M")  > -1)
        {
            throw new Exception("Name Error");
        }
        await Task.Delay(500);
        return $"Hello, {name}!";
    }

    public string SayHelloDirectly(string name)
    {
        if (name.IndexOf("M") > -1)
        {
            throw new Exception("Name Error");
        }
        return $"Hello, {name}!";
    }
}

使用IHelloService,只需要正常的注入IHelloService即可

@page "/counter"
@using Blazor.Test.Client.Services
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
SayHello Response: @response
@code {
    private int currentCount = 0;
    string? response;
    [Inject, NotNull] IHelloService? Service { get; set; }

    private async Task IncrementCount()
    {
        currentCount++;
        response = await Service.SayHelloAsync($"Marvel{currentCount}");
    }
}

当然,要想获取服务的时候能获取到咱们自己生成的代理类,还需要一步设置

// 服务注册不必多说
builder.Services.AddScoped<IHelloService, HelloService>();
builder.Services.AddScoped<TestAop>();
builder.Services.AddScoped<ExceptionAop>();
// 关键是这里
builder.Host.UseServiceProviderFactory(new AutoAopProxyGenerator.AutoAopProxyServiceProviderFactory());

最后附上生成的代码,处理类的调用逻辑跟Asp.Net Core的中间件模式一样

using AutoAopProxyGenerator;
// <auto-generated/>
#pragma warning disable
#nullable enable
namespace Blazor.Test.Client.Services
{
    [global::System.CodeDom.Compiler.GeneratedCode("AutoAopProxyGenerator.AutoAopProxyClassGenerator", "1.0.0.0")]
    /// <inheritdoc/>
    public partial class HelloServiceGeneratedProxy : Blazor.Test.Client.Services.IHelloService
    {
        private readonly Blazor.Test.Client.Services.HelloService proxy;

        private readonly Blazor.Test.Client.Aop.ExceptionAop ExceptionAop;

        private readonly Blazor.Test.Client.Aop.TestAop TestAop;

        public HelloServiceGeneratedProxy(Blazor.Test.Client.Services.HelloService proxy, Blazor.Test.Client.Aop.ExceptionAop ExceptionAop, Blazor.Test.Client.Aop.TestAop TestAop)
        {
            this.proxy = proxy;
            this.ExceptionAop = ExceptionAop;
            this.TestAop = TestAop;
        }

        [global::System.CodeDom.Compiler.GeneratedCode("AutoAopProxyGenerator.AutoAopProxyClassGenerator", "1.0.0.0")]
        public async System.Threading.Tasks.Task<string> SayHelloAsync(string name)
        {
            string returnValue = default;
            async System.Threading.Tasks.Task Done(ProxyContext ctx)
            {
                returnValue = await proxy.SayHelloAsync(name);
                ctx.Executed = true;
                ctx.ReturnValue = returnValue;
            }
            var builder = AsyncPipelineBuilder<ProxyContext>.Create(Done);
            builder.Use(ExceptionAop.Invoke);
            builder.Use(TestAop.Invoke);
            var job = builder.Build();
            var context = ContextHelper<Blazor.Test.Client.Services.IHelloService, Blazor.Test.Client.Services.HelloService>.GetOrCreate(nameof(SayHelloAsync), [typeof(string)]);
            context.HasReturnValue = true;
            context.Parameters = new object?[] {name};
            await job.Invoke(context);
            return returnValue;
        }

        [global::System.CodeDom.Compiler.GeneratedCode("AutoAopProxyGenerator.AutoAopProxyClassGenerator", "1.0.0.0")]
        public string SayHelloDirectly(string name)
          => proxy.SayHelloDirectly(name);
    }
}
posted @ 2024-08-27 15:31  yaoqinglin_mtiter  阅读(31)  评论(0编辑  收藏  举报