Castle was born in 2003 from the Apache Avalon project with the goal of creating an IOC framework. There are four components now:
- ORM Components: ActiveRecord
- IOC Components: Windsor
- Dynamic Proxy Component: DynamicProxy
- Web MVC Component: MonoRail
This article mainly introduces the dynamic proxy components
Basic usage
It is achieved through Emit reflection dynamically generating proxy classes. The efficiency is slower than static implantation, but it is higher than ordinary reflections. Dynamic proxy only takes effect on public interface methods and virtual methods in classes, because only methods in interfaces and virtual methods in classes can be rewritten in subclasses.
Interface-based interceptor
public interface IProductRepository { void Add(string name); } public class ProductRepository : IProductRepository { public void Add(string name) => ($"New products:{name}"); } public class LoggerInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { var methodName = ; ($"{methodName} Before execution"); //Calling business methods (); ($"{methodName} Execution is completed"); } } class Program { static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(); IProductRepository productRepo = new ProductRepository(); IProductRepository proxy = (productRepo, loggerIntercept); ("Rice"); (); } }
Class-based interceptor
public class ProductRepository { public virtual void Add(string name) => ($"New products:{name}"); } static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(); ProductRepository proxy = (new ProductRepository(), loggerIntercept); // Use the CreateClassProxy generic method to eliminate instantiation code //ProductRepository proxy = <ProductRepository>(loggerIntercept); ("Rice"); }
In the above example, if it is not a virtual method, there will be no error, but the interceptor will not be called.
Asynchronous function interception
There is no difference between intercepting asynchronous functions and synchronization, but if you want to insert content after the method is executed, you need await
public class ProductRepository { public virtual Task Add(string name) { return (() => { (1000); ($"Added new products asynchronously:{name}"); }); } } public class LoggerInterceptor : IInterceptor { public async void Intercept(IInvocation invocation) { var methodName = ; ($"{methodName} Before execution"); (); // If you do not await, you will first output "execution completed" and then output "Async-added new product" var task = (Task); await task; ($"{methodName} Execution is completed"); } }
The above writing method is simple and crude. If the return value is Task<TResult> or not an asynchronous function, an error will occur. So here we need to make a judgment on the return value.
You can use the package, which wraps Castle, making asynchronous calls easier.
GitHub address:/JSkimming/
public class ProductRepository : IProductRepository { public Task Add(string name) { return (() => { (1000); ($"Added new products asynchronously:{name}"); }); } public Task<string> Get() { return (() => { (1000); ($"Get Products"); return "Rice"; }); } } public class LoggerInterceptor : IAsyncInterceptor { public void InterceptAsynchronous(IInvocation invocation) { = InternalInterceptAsynchronous(invocation); } async Task InternalInterceptAsynchronous(IInvocation invocation) { var methodName = ; ($"{methodName} Before asynchronous execution"); (); await (Task); ($"{methodName} Asynchronous execution is completed"); } public void InterceptAsynchronous<TResult>(IInvocation invocation) { = InternalInterceptAsynchronous<TResult>(invocation); (((Task<TResult>)).Id); } private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) { var methodName = ; ($"{methodName} Before asynchronous execution"); (); var task = (Task<TResult>); TResult result = await task; (); ($"{methodName} Asynchronous execution is completed"); return result; } public void InterceptSynchronous(IInvocation invocation) { var methodName = ; ($"{methodName} Before synchronous execution"); (); ($"{methodName} Synchronous execution is completed"); } } class Program { static void Main(string[] args) { ProxyGenerator generator = new ProxyGenerator(); IAsyncInterceptor loggerIntercept = new LoggerInterceptor(); IProductRepository productRepo = new ProductRepository(); IProductRepository proxy = (productRepo, loggerIntercept); (); } }
This is the example writing method provided. There is a question here and it is also my doubt. = InternalInterceptAsynchronous(invocation); will cause the agent to return the Task to be a new Task, which we can output to confirm. Personally, I feel a bit overwhelming.
public async void InterceptAsynchronous<TResult>(IInvocation invocation) { var methodName = ; ($"{methodName} Before asynchronous execution"); (); var task = (Task<TResult>); await task; ($"{methodName} Asynchronous execution is completed"); }
That's pretty good.
If any friend knows why he wants to return a new Task, please leave a message to tell me, thank you!
Autofac integration
is an Autofac extension that provides AOP interception with Castle.
Interface-based interceptor
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //Register interceptor <LoggerInterceptor>().AsSelf(); //Register the service to be blocked <ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //Enable interface interception .InterceptedBy(typeof(LoggerInterceptor)); //Specify the interceptor IContainer container = (); IProductRepository productRepo = <IProductRepository>(); ("Rice"); }
Class-based interceptor
static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); //Register interceptor <LoggerInterceptor>().AsSelf(); //Register the service to be blocked <ProductRepository>() .EnableClassInterceptors() //Enable class interception .InterceptedBy(typeof(LoggerInterceptor)); //Specify the interceptor IContainer container = (); ProductRepository productRepo = <ProductRepository>(); ("Rice"); }
Asynchronous function interception
In this case, the IAsyncInterceptor interface does not integrate the IInterceptor interface, but is bound to Castle, so an error will be reported according to the above synchronous intercepting writing method.
IAsyncInterceptor provides the ToInterceptor() extension method for type conversion.
public class LoggerInterceptor : IInterceptor { readonly LoggerAsyncInterceptor interceptor; public LoggerInterceptor(LoggerAsyncInterceptor interceptor) { = interceptor; } public void Intercept(IInvocation invocation) { ().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 <LoggerInterceptor>().AsSelf(); <LoggerAsyncInterceptor>().AsSelf(); //Register the service to be blocked <ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //Enable interface interception .InterceptedBy(typeof(LoggerInterceptor)); //Specify the interceptor var container = (); IProductRepository productRepo = <IProductRepository>(); (); }
The above is the detailed content of C# using Castle to implement AOP and how to integrate Castle with Autofac. For more information about C# using Castle to implement AOP, please pay attention to my other related articles!