[c#] use castle to implement AOP and Autofac to integrate castle

Time:2021-8-6

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 focuses on dynamic agent componentsCastle.DynamicProxy

Basic Usage

Castle.DynamicProxyIt 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 product: {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} execution completed");
    }
}

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 product: {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 instantiation code
    //ProductRepository proxy = generator.CreateClassProxy(loggerIntercept);

    Proxy. Add ("rice");
}

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

Asynchronous function interception

Castle.DynamicProxyThe interception of asynchronous functions is no different from synchronization, but if you want to insert content after the method is executed, you need toawait

public class ProductRepository
{
    public virtual Task Add(string name)
    {
        return Task.Run(() =>
                        {
                            Thread.Sleep(1000);
                            Console.writeline ($"asynchronous new product: {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 enabled, 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} execution completed");
    }
}

The above method is simple and rough. If the return value isTask, or if it is not an asynchronous function, an error will occur. So here is to judge the return value.

have access toCastle.Core.AsyncInterceptorBag, it’s packedCastle, making asynchronous calls easier.

Castle.Core.AsyncInterceptorGitHub address: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 product: {name}");
                        });
    }

    public Task Get()
    {
        return Task.Run(() =>
                        {
                            Thread.Sleep(1000);
                            Console.writeline ($"get product");

                            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(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous(invocation);

        Console.WriteLine(((Task)invocation.ReturnValue).Id);
    }

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

        Console.writeline ($"{methodname} before asynchronous execution");

        invocation.Proceed();
        var task = (Task)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 isCastle.Core.AsyncInterceptorThere is a problem with the example writing method provided, which is also my doubt.invocation.ReturnValue = InternalInterceptAsynchronous(invocation);Will cause the agent to returnTaskIt’s a new oneTask, we can output thisTask.IdTo confirm. Personally, I think it’s a little superfluous.

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

    Console.writeline ($"{methodname} before asynchronous execution");

    invocation.Proceed();
    var task = (Task)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.DynamicProxyIt’s aAutofacExpandable withCastleProvide AOP interception together.

Interface based interceptor

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

    //Register services to block
    builder.RegisterType().AsImplementedInterfaces()
        . enableinterfaceinterceptors() // enable interface interception
        .InterceptedBy(typeof(LoggerInterceptor));      // Designated interceptor

    IContainer container = builder.Build();

    IProductRepository productRepo = container.Resolve();

    Productrepo. Add ("rice");
}

Class based interceptor

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

    //Register services to block
    builder.RegisterType()
        . enableclassinterceptors() // enable class interception
        .InterceptedBy(typeof(LoggerInterceptor));      // Designated interceptor

    IContainer container = builder.Build();

    ProductRepository productRepo = container.Resolve();

    Productrepo. Add ("rice");
}

Asynchronous function interception

Castle.Core.AsyncInterceptorIn,IAsyncInterceptorInterfaces are not integratedIInterceptorInterface, andAutofac.Extras.DynamicProxyIs bindingCastleYes, so if you follow the above description of synchronous interception, an error will be reported.

IAsyncInterceptorProvidedToInterceptor()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(IInvocation invocation)
    {
        //...
    }

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

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

    //Register services to block
    builder.RegisterType().AsImplementedInterfaces()
        . enableinterfaceinterceptors() // enable interface interception
        .InterceptedBy(typeof(LoggerInterceptor));      // Designated interceptor

    var container = builder.Build();

    IProductRepository productRepo = container.Resolve();

    productRepo.Get();
}

reference resources

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