Use castle to implement AOP and Autofac to integrate Castle

Castle was born in the Apache Avalon project in 2003 to create an IOC framework. Up to now, there are four components:

  • ORM component: ActiveRecord
  • IOC component: Windsor
  • Dynamic proxy component: DynamicProxy
  • Web MVC component: MonoRail

This article mainly introduces the dynamic agent component castle DynamicProxy

Basic Usage

Castle.DynamicProxy is realized by dynamically generating proxy classes through Emit reflection. The efficiency is slower than static implantation, but higher than ordinary reflection. Dynamic proxy only works for public interface methods and virtual methods in classes, because only methods in interfaces and virtual methods in classes can be overridden in subclasses.

Interface based interceptor

public interface IProductRepository
{
    void Add(string name);
}

public class ProductRepository : IProductRepository
{
    public void Add(string name) => Console.WriteLine($"New products:{name}");
}

public class LoggerInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;

        Console.WriteLine($"{methodName} Before execution");

        //Call business method
        invocation.Proceed();

        Console.WriteLine($"{methodName} completion of enforcement");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ProxyGenerator generator = new ProxyGenerator();

        IInterceptor loggerIntercept = new LoggerInterceptor();

        IProductRepository productRepo = new ProductRepository();

        IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);

        proxy.Add("rice");

        Console.Read();
    }
}

Class based interceptor

public class ProductRepository
{
    public virtual void Add(string name) => Console.WriteLine($"New products:{name}");
}

static void Main(string[] args)
{
    ProxyGenerator generator = new ProxyGenerator();

    IInterceptor loggerIntercept = new LoggerInterceptor();

    ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept);
    // Using the CreateClassProxy generic method eliminates the instantiation code
    //ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);

    proxy.Add("rice");
}

In the above example, if ProductRepository Add is not a virtual method and will not report an error, but the interceptor will not be called.

Asynchronous function interception

Castle. The interception of asynchronous functions by dynamicproxy is no different from that of synchronization. However, if you want to insert content after the method is executed, you need await

public class ProductRepository
{
    public virtual Task Add(string name)
    {
        return Task.Run(() =>
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine($"Asynchronous new products:{name}");
                        });
    }
}

public class LoggerInterceptor : IInterceptor
{
    public async void Intercept(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;

        Console.WriteLine($"{methodName} Before execution");

        invocation.Proceed();

        // If await is not used, the "execution completed" will be output first, and then the "asynchronous new product" will be output
        var task = (Task)invocation.ReturnValue;
        await task;

        Console.WriteLine($"{methodName} completion of enforcement");
    }
}

The above method is simple and crude. If the return value is task < tresult >, or it is not an asynchronous function, an error will occur. So here is to judge the return value.

You can use Castle Core. Asyncinterceptor package, which wraps Castle to make asynchronous calls easier.

Castle. Core. GitHub address of asyncinterceptor: https://github.com/JSkimming/Castle.Core.AsyncInterceptor

public class ProductRepository : IProductRepository
{
    public Task Add(string name)
    {
        return Task.Run(() =>
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine($"Asynchronous new products:{name}");
                        });
    }

    public Task<string> Get()
    {
        return Task.Run(() =>
                        {
                            Thread.Sleep(1000);
                            Console.WriteLine($"Get products");

                            return "rice";
                        });
    }
}

public class LoggerInterceptor : IAsyncInterceptor
{
    public void InterceptAsynchronous(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
    }

    async Task InternalInterceptAsynchronous(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;

        Console.WriteLine($"{methodName} Before asynchronous execution");

        invocation.Proceed();
        await (Task)invocation.ReturnValue;

        Console.WriteLine($"{methodName} Asynchronous execution completed");
    }

    public void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);

        Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id);
    }

    private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;

        Console.WriteLine($"{methodName} Before asynchronous execution");

        invocation.Proceed();
        var task = (Task<TResult>)invocation.ReturnValue;
        TResult result = await task;

        Console.WriteLine(task.Id);

        Console.WriteLine($"{methodName} Asynchronous execution completed");

        return result;
    }

    public void InterceptSynchronous(IInvocation invocation)
    {
        var methodName = invocation.Method.Name;

        Console.WriteLine($"{methodName} Before synchronization");

        invocation.Proceed();

        Console.WriteLine($"{methodName} Synchronization completed");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ProxyGenerator generator = new ProxyGenerator();

        IAsyncInterceptor loggerIntercept = new LoggerInterceptor();

        IProductRepository productRepo = new ProductRepository();

        IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);

        proxy.Get();
    }
}

This is castle Core. There is a problem with the example writing provided by asyncinterceptor, which is also my doubt. invocation.ReturnValue = InternalInterceptAsynchronous(invocation); The Task that will cause the agent to return is a new Task, which we can output ID to confirm. Personally, I think it's a little superfluous.

public async void InterceptAsynchronous<TResult>(IInvocation invocation)
{
    var methodName = invocation.Method.Name;

    Console.WriteLine($"{methodName} Before asynchronous execution");

    invocation.Proceed();
    var task = (Task<TResult>)invocation.ReturnValue;
    await task;

    Console.WriteLine($"{methodName} Asynchronous execution completed");
}

That's good.

If you know why you want to return a new Task, please leave a message and let me know. Thank you!

Autofac integration

Autofac.Extras.DynamicProxy is an Autofac extension that provides AOP interception with Castle.

Interface based interceptor

static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();
    //Register interceptor
    builder.RegisterType<LoggerInterceptor>().AsSelf();

    //Register services to block
    builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
        .EnableInterfaceInterceptors()                  //Enable interface interception
        .InterceptedBy(typeof(LoggerInterceptor));      //Designated interceptor

    IContainer container = builder.Build();

    IProductRepository productRepo = container.Resolve<IProductRepository>();

    productRepo.Add("rice");
}

Class based interceptor

static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();
    //Register interceptor
    builder.RegisterType<LoggerInterceptor>().AsSelf();

    //Register services to block
    builder.RegisterType<ProductRepository>()
        .EnableClassInterceptors()                      //Enable class interception
        .InterceptedBy(typeof(LoggerInterceptor));      //Designated interceptor

    IContainer container = builder.Build();

    ProductRepository productRepo = container.Resolve<ProductRepository>();

    productRepo.Add("rice");
}

Asynchronous function interception

Castle. Core. In asyncinterceptor, IAsyncInterceptor interface does not integrate IInterceptor interface, but Autofac Extras. Dynamicproxy is bound to Castle, so an error will be reported according to the above description of synchronous interception.

IAsyncInterceptor provides ToInterceptor() extension method for type conversion.

public class LoggerInterceptor : IInterceptor
{
    readonly LoggerAsyncInterceptor interceptor;

    public LoggerInterceptor(LoggerAsyncInterceptor interceptor)
    {
        this.interceptor = interceptor;
    }

    public void Intercept(IInvocation invocation)
    {
        this.interceptor.ToInterceptor().Intercept(invocation);
    }
}

public class LoggerAsyncInterceptor : IAsyncInterceptor
{
    public void InterceptAsynchronous(IInvocation invocation)
    {
        //...
    }

    public void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        //...
    }

    public void InterceptSynchronous(IInvocation invocation)
    {
        //...
    }
}

static void Main(string[] args)
{
    ContainerBuilder builder = new ContainerBuilder();
    //Register interceptor
    builder.RegisterType<LoggerInterceptor>().AsSelf();
    builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();

    //Register services to block
    builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
        .EnableInterfaceInterceptors()                  //Enable interface interception
        .InterceptedBy(typeof(LoggerInterceptor));      //Designated interceptor

    var container = builder.Build();

    IProductRepository productRepo = container.Resolve<IProductRepository>();

    productRepo.Get();
}

reference resources

https://www.cnblogs.com/youring2/p/10962573.html

Tags: C#

Posted by david-remone123 on Sun, 17 Apr 2022 07:03:53 +0930