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