Three Implementations of AOP in.NET
Preface
Has been busy with the code, rare time, study the next AOP, share with you, also easy to view later. ()~*
Because the IOC container integrated in. NET CORE is Castle, Autofac is commonly used everyday, so I only studied the default implementation of. NET Emoting, Castle and Autofac. Castle has two plug-ins, Castle.Windsor (IOC container) and Castle.Core (core). Autofac's interceptor is the dynamic proxy of Castle.Core.
Basic concepts
AOP
AOP(Aspect Oriented Programming): Aspect Oriented Programming is the continuation of OOP, which is used to add functions dynamically without modifying the code, separating core logic from non-core logic. Logging is one of the most common usage scenarios of AOP.
IOC and DI
DI(Dependency Injection) dependency injection is actually an implementation of IOC (Controlled Flip). It is called dependency injection because the dependency relationship between the caller and the callee is injected through a third party, which is usually called IOC container. IOC is a kind of idea. The principle of inversion of dependence advocates that programs should rely on abstraction rather than on concrete implementation in order to reduce coupling. The interface that controls the implementation is usually the caller. There is still a dependency between the caller and the concrete implementation. Now, the idea of IOC is to give this control to a third party. The reason for this is that the implementation of dynamic proxy often relies on IOC containers.
Ways of realization
AOP interceptors can be implemented in two ways: dynamic proxy and static proxy.
Dynamic Agent:
It is the dynamic addition of entry points in the running of programs, which is the reason why IOC containers are usually needed.
Advantages and disadvantages: Compared with static proxy, efficiency is lower, but flexibility is strong, and no additional compilation is required.
The three ways we talk about are all implemented by dynamic agents.
Static proxy:
It is inserted in the pre-compiler stage of the program.
Advantages and disadvantages: Compared with dynamic proxy, performance is higher, but flexibility is poor, and additional compilation is required.
Remoting implementation
- Namespaces to be referenced
using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies;
- Because the implementation is relatively simple, I pasted all the code below.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; using System.Text; using System.Threading.Tasks; /// <summary> /// Default implementation (Remoting) /// </summary> namespace DefaultDemo { class Program { static void Main(string[] args) { var per = new Proxy<King>(new King()).GetTransparentProxy() as King; if (per != null) { var str = per.RuleTheCastle("fffff"); } Console.ReadKey(); } } /// <summary> /// Basic types /// </summary> class King : MarshalByRefObject { public string RuleTheCastle(string str) { str = "hello word," + str; return str; } } public class Proxy<T> : RealProxy where T : new() { private object _obj; public Proxy(object obj) : base(typeof(T)) { _obj = obj; } public override IMessage Invoke(IMessage msg) { IDictionary oProperties = msg.Properties; object oMethodName = null; if (oProperties.Contains("__MethodName")) { oMethodName = oProperties["__MethodName"]; } Console.WriteLine("Method name:{0}", oMethodName); //Participation object[] oArguments; if (oProperties.Contains("__Args")) { oArguments = oProperties["__Args"] as object[]; } else { oArguments = new object[0]; } foreach (var oArgument in oArguments) { Console.WriteLine("Participation:{0}", oArgument); } //Invocation and parameterization object oReturnValue = ((IMethodCallMessage)msg).MethodBase.Invoke(_obj, oArguments); Console.WriteLine("Ginseng production:{0}", oReturnValue); return new ReturnMessage(oReturnValue, null, 0, null, null); } } }
Castle implementation
- Install the Castle.Windsor package in NuGet Manager
The Castle.Windsor and Castle.Core class libraries are automatically referenced when the installation is complete - Reference to the corresponding namespace
using Castle.Core; using Castle.DynamicProxy; using Castle.MicroKernel.Registration; using Castle.MicroKernel.SubSystems.Configuration; using Castle.Windsor;
Implementation of Dependency Injection
- Add an abstract interface and its implementation class
public interface IKing { string RuleTheCastle(string name); } class King : IKing { public string Name { get; set; } public int Age { get; set; } public string RuleTheCastle(string name) { Console.WriteLine("Method executed"); return "Hello Word," + name; } }
2. To add an installation class Repositories Installer, you need to inherit the IWindsor Installer interface and register components in it.
public class RepositoriesInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For<IKing>().ImplementedBy<King>().LifestyleSingleton() ); } }
- Instantiate a container and run the installer so that they can register components in the container
IWindsorContainer container = new WindsorContainer(); container.Install(new RepositoriesInstaller());
- Get component instances, dependency injection
var king = container.Resolve<IKing>(); string ret = king.RuleTheCastle("tzj");
- Release IOC container
container.Dispose();
Adding interceptors
- Declare a log interceptor class LoggingInterceptor and let him inherit the IInterceptor interface
public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { try { //type var type = invocation.TargetType; //object var oObject = invocation.InvocationTarget; //Method string strMethodName = invocation.Method.Name; Console.WriteLine("The method invoked is:" + strMethodName); //Participation var oArguments = invocation.Arguments; if (oArguments != null && oArguments.Length > 0) { for (int i = 0; i < oArguments.Length; i++) { Console.WriteLine("Participation{0}:{1}", i, oArguments[i]); } } //Execute this method, formally invoke the method //Access before execution //After successful execution, parameters are available invocation.Proceed(); //Output parameters and ref can only be achieved by comparing them. //Ginseng production var oReturnValue = invocation.ReturnValue; Console.WriteLine("Participants:" + oReturnValue); } catch (Exception ex) { //Whether it's an error in the intercepted method or an error in the intercepted method, it's going to be here. Console.WriteLine("error message:" + ex.ToString()); } } }
2. To add the corresponding interceptor property to the implementation class, it is important to note that the implementation class must be controlled by the IOC container.
[Interceptor(typeof(LoggingInterceptor))]
- The interceptor will also be registered in the container
Component.For<LoggingInterceptor>()
That's done.
Complete code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Castle.Core; using Castle.DynamicProxy; using Castle.MicroKernel.Registration; using Castle.MicroKernel.SubSystems.Configuration; using Castle.Windsor; namespace CastleDemo { class Program { static void Main(string[] args) { IWindsorContainer container = new WindsorContainer(); container.Install(new RepositoriesInstaller()); var king = container.Resolve<IKing>(); string ret = king.RuleTheCastle("tzj"); Console.WriteLine(ret); container.Dispose(); Console.ReadKey(); } } public interface IKing { string RuleTheCastle(string name); } //The class of the section must be controlled by the container. [Interceptor(typeof(LoggingInterceptor))] class King : IKing { public string Name { get; set; } public int Age { get; set; } public string RuleTheCastle(string name) { Console.WriteLine("Method executed"); return "Hello Word," + name; } } public class RepositoriesInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For<LoggingInterceptor>(), Component.For<IKing>().ImplementedBy<King>().LifestyleSingleton() ); } } public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { try { //type var type = invocation.TargetType; //object var oObject = invocation.InvocationTarget; //Method string strMethodName = invocation.Method.Name; Console.WriteLine("The method invoked is:" + strMethodName); //Participation var oArguments = invocation.Arguments; if (oArguments != null && oArguments.Length > 0) { for (int i = 0; i < oArguments.Length; i++) { Console.WriteLine("Participation{0}:{1}", i, oArguments[i]); } } //Execute this method, formally invoke the method //Access before execution //After successful execution, parameters are available invocation.Proceed(); //Output parameters and ref can only be achieved by comparing them. //Ginseng production var oReturnValue = invocation.ReturnValue; Console.WriteLine("Participants:" + oReturnValue); } catch (Exception ex) { //Whether it's an error in the intercepted method or an error in the intercepted method, it's going to be here. Console.WriteLine("error message:" + ex.ToString()); } } } }
Implementation of Autofac
- Install the Autofac.Extras.DynamicProxy package in NuGet Manager
Programs are automatically referenced - Referencing the required namespace
using Autofac; using Autofac.Extras.DynamicProxy; using Castle.DynamicProxy;
Implementation of Dependency Injection
This one is similar to the one above. Paste the code directly.
private static IContainer Container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<King>().As<IKing>(); Container = builder.Build(); using (var scope = Container.BeginLifetimeScope()) { var writer = scope.Resolve<IKing>(); writer.RuleTheCastle("aaa"); } Console.Read(); } } public interface IKing { string RuleTheCastle(string name); } public class King : IKing { public string Name { get; set; } public int Age { get; set; } public string RuleTheCastle(string name) { Console.WriteLine("Method executed"); return "Hello Word," + name; } }
Adding interceptors
Interceptors are similar
- Add interceptor attributes
[Intercept(typeof(LoggingInterceptor))]
- Enable proxies when registering components
Enabling class proxy requires that the permissions of the faceted class be public and that the method is virtual
builder.RegisterType<King>().As<IKing>().EnableClassInterceptors();
Enabling Interface Agent
builder.RegisterType<King>().As<IKing>().EnableInterfaceInterceptors();
- The interceptor also needs to be injected into the container.
builder.RegisterType<LoggingInterceptor>();
Complete code
using Autofac; using Autofac.Extras.DynamicProxy; using Castle.DynamicProxy; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AutofacDemo { class Program { private static IContainer Container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<LoggingInterceptor>(); //EnableClassInterceptors Enables Class Agent Requirements Method is virtual //builder.RegisterType<King>().As<IKing>().EnableClassInterceptors(); //Enable Interface Interceptors Enables Interface Agents builder.RegisterType<King>().As<IKing>().EnableInterfaceInterceptors(); Container = builder.Build(); using (var scope = Container.BeginLifetimeScope()) { var writer = scope.Resolve<IKing>(); writer.RuleTheCastle("aaa"); } Console.Read(); } } public interface IKing { string RuleTheCastle(string name); } //The class of the section must be controlled by the container. [Intercept(typeof(LoggingInterceptor))] public class King : IKing { public string Name { get; set; } public int Age { get; set; } public string RuleTheCastle(string name) { Console.WriteLine("Method executed"); return "Hello Word," + name; } } public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { try { //type var type = invocation.TargetType; //object var oObject = invocation.InvocationTarget; //Method string strMethodName = invocation.Method.Name; Console.WriteLine("The method invoked is:" + strMethodName); //Participation var oArguments = invocation.Arguments; if (oArguments != null && oArguments.Length > 0) { for (int i = 0; i < oArguments.Length; i++) { Console.WriteLine("Participation{0}:{1}", i, oArguments[i]); } } //Execute this method, formally invoke the method //Access before execution //After successful execution, parameters are available invocation.Proceed(); //Output parameters and ref can only be achieved by comparing them. //Ginseng production var oReturnValue = invocation.ReturnValue; Console.WriteLine("Participants:" + oReturnValue); } catch (Exception ex) { //Whether it's an error in the intercepted method or an error in the intercepted method, it's going to be here. Console.WriteLine("error message:" + ex.ToString()); } } } }
Epilogue
Some parts of the article are not so detailed, but most of the notes are, I will be sorry if I have time to add more details. (_)
For the first time, I wrote a blog. If there are any problems, I would like to ask the big guys to give me advice. ()