1. AOP Concepts
Official Interpretation: AOP (Aspect-Oriented Programming) is a technology that dynamically and uniformly adds functionality to a program without modifying its source code through precompilation and run-time dynamic proxies.It is a new methodology and a complement to traditional OOP programming.OOP is concerned with dividing requirement functions into different, relatively independent, well-encapsulated classes and letting them have their own behavior, relying on Inheritance and polymorphism to define their relationship. AOP is the hope that it can separate common requirement functions from unrelated classes and enable many classes to share a single behavior without modifying many classes as changes occur.You just need to modify this behavior.AOP uses aspects to modularize cross-cutting concerns, and OOP uses classes to modularize States and behaviors.In the world of OOP, programs are organized through classes and interfaces, and it is appropriate to use them to implement the core business logic of a program, but it is very difficult to achieve cross-cutting concerns (functional requirements that span multiple modules of an application), such as logging, privilege validation, exception interception, and so on.
Personal understanding: AOP is to extract the public functions, if the requirements of public functions change in the future, you only need to change the code of the public module, and there is no need to change where multiple calls are made.Face-oriented means focusing only on common functions, not on business logic.It typically works by intercepting, for example, projects that typically have privilege validation capabilities that verify whether the current logged-in user has permission to view the interface before entering each page.It is impossible to say that this validation code is written in the initialization method of each page, and our AOP will come in handy at this time.The mechanism of AOP is to predefine a set of attributes so that it has the ability to intercept methods that allow you to do what you want before and after executing a method, whereas all we need to do is add a feature to the corresponding method or class definition.
2. AOP Advantage
1) By separating common functions from business logic, a large number of duplicate codes can be omitted, which is conducive to code operation and maintenance.
2) In software design, extract common functions (facets) to facilitate the modularization of software design and reduce the complexity of software architecture.That is to say, a common function is a separate module, the design code of these common functions can not be seen in the main business of the project.
3. AOP Application
3.1. Static proxy mode
3.1.1. Implement static proxy using decorator mode
1) Create a new class: DecoratorAOP.cs
/// <summary> /// Implement static proxy using decorator mode /// </summary> public class DecoratorAOP { /// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// User Registration Interface /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// User Registration Interface Implementation Class /// </summary> public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine($"User registration was successful. Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// Decorator Mode Implementation AOP function /// </summary> public class UserProcessorDecorator : IUserProcessor { private IUserProcessor UserProcessor { get; set; } public UserProcessorDecorator(IUserProcessor userProcessor) { UserProcessor = userProcessor; } public void RegUser(User user) { PreProceed(user); UserProcessor.RegUser(user); PostProceed(user); } public void PreProceed(User user) { Console.WriteLine("Before method execution"); } public void PostProceed(User user) { Console.WriteLine("After method execution"); } } /// <summary> /// Run tests /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; IUserProcessor processor = new UserProcessorDecorator(new UserProcessor()); processor.RegUser(user); } }
2) Call:
static void Main(string[] args) { #region Implement static proxy using decorator mode DecoratorAOP.Show(); Console.Read(); #endregion }
3) The results are as follows:
The above code is an example of simulated user registration: before submitting registration information, you need to do some preparatory work, such as data validity check, and after submitting registration information, you need to do some logging.From the code above, we can see that we manually let the method do what we need before and after it is executed by static seeding.
3.1.2. Implementing static proxy using proxy mode
1) Create a new class: ProxyAOP.cs
/// <summary> /// Implement static proxy using proxy mode /// </summary> public class ProxyAOP { /// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// User Registration Interface /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// User Registration Interface Implementation Class /// </summary> public class UserProcessor : IUserProcessor { public void RegUser(User user) { Console.WriteLine($"User registration was successful. Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// Proxy Mode Implementation AOP function /// </summary> public class UserProcessorProxy : IUserProcessor { private IUserProcessor userProcessor = new UserProcessor(); public void RegUser(User user) { PreProceed(user); userProcessor.RegUser(user); PostProceed(user); } private void PreProceed(User user) { Console.WriteLine("Before method execution"); } private void PostProceed(User user) { Console.WriteLine("After method execution"); } } public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; IUserProcessor processor = new UserProcessorProxy(); processor.RegUser(user); } }
2) Call:
static void Main(string[] args) { #region Implement static proxy using proxy mode ProxyAOP.Show(); Console.Read(); #endregion }
3) The results are as follows:
3.2. Dynamic Agent Method
3.2.1. Implement dynamic proxy using.Net Remoting/RealProxy
1) Create a new class: RealProxyAOP.cs
/// <summary> /// Use.Net Remoting/RealProxy Implement dynamic proxy /// Client - TransparentProxy - RealProxy - Target Object /// Limited to business class must be inherited from MarshalByRefObject type /// </summary> public class RealProxyAOP { /// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// User Registration Interface /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// User Registration Interface Implementation Class /// Must inherit from MarshalByRefObject Parent class, otherwise it cannot be generated. /// </summary> public class UserProcessor : MarshalByRefObject, IUserProcessor { public void RegUser(User user) { Console.WriteLine($"User registration was successful. Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// Real Agent: Provides the basic functions of an agent /// </summary> public class MyRealProxy<T> : RealProxy { private T _target; public MyRealProxy(T target) : base(typeof(T)) { _target = target; } public override IMessage Invoke(IMessage msg) { PreProceed(msg); IMethodCallMessage callMessage = (IMethodCallMessage)msg; object returnValue = callMessage.MethodBase.Invoke(_target, callMessage.Args); PostProceed(msg); return new ReturnMessage(returnValue, new object[0], 0, null, callMessage); } public void PreProceed(IMessage msg) { Console.WriteLine("Before method execution"); } public void PostProceed(IMessage msg) { Console.WriteLine("After method execution"); } } /// <summary> /// Transparent Proxy: Provides the illusion that the actual object resides in client space /// </summary> public static class TransparentProxy { public static T Create<T>() { T instance = Activator.CreateInstance<T>(); MyRealProxy<T> realProxy = new MyRealProxy<T>(instance); T transparentProxy = (T)realProxy.GetTransparentProxy(); return transparentProxy; } } /// <summary> /// Run tests /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; UserProcessor processor = TransparentProxy.Create<UserProcessor>(); processor.RegUser(user); } }
2) Call:
static void Main(string[] args) { #region Use.Net Remoting/RealProxy Implement dynamic proxy RealProxyAOP.Show(); Console.Read(); #endregion }
3) The results are as follows:
3.2.2. Using Castle\DynamicProxy to implement dynamic proxy
1) Install Castle.Core in NuGet.
2) Create a new class: CastleProxyAOP.cs
/// <summary> /// Use Castle\DynamicProxy Implement dynamic proxy /// Method must be virtual /// </summary> public class CastleProxyAOP { /// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// User Registration Interface /// </summary> public interface IUserProcessor { void RegUser(User user); } /// <summary> /// User Registration Interface Implementation Class /// </summary> public class UserProcessor : IUserProcessor { /// <summary> /// You must bring it with you virtual,Otherwise, it is invalid. /// </summary> /// <param name="user"></param> public virtual void RegUser(User user) { Console.WriteLine($"User registration was successful. Name:{user.Name} Password:{user.Password}"); } } /// <summary> /// Interceptor /// </summary> public class MyInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { PreProceed(invocation); invocation.Proceed(); PostProceed(invocation); } public void PreProceed(IInvocation invocation) { Console.WriteLine("Before method execution"); } public void PostProceed(IInvocation invocation) { Console.WriteLine("After method execution"); } } /// <summary> /// Run tests /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "World" }; ProxyGenerator generator = new ProxyGenerator(); MyInterceptor interceptor = new MyInterceptor(); UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor); userprocessor.RegUser(user); } }
3) Call:
static void Main(string[] args) { #region Use Castle\DynamicProxy Implement dynamic proxy CastleProxyAOP.Show(); Console.Read(); #endregion }
4) The results are as follows:
3.2.3, AOP implementation using EntLib\PIAB Unity (non-configurable)
1) Install Unity and Unity.Interception in NuGet.
2) Create a new class: UnityAOP.cs
/// <summary> /// Use EntLib\PIAB Unity Implement dynamic proxy(Non-Configurable) /// </summary> public class UnityAOP { #region business /// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } } /// <summary> /// User Registration Interface /// </summary> [ExceptionHandler(Order = 1)] [LogHandler(Order = 2)] [UserHandler(Order = 3)] [AfterLogHandler(Order = 5)] public interface IUserProcessor { void RegUser(User user); } /// <summary> /// User Registration Interface Implementation Class /// </summary> public class UserProcessor : IUserProcessor //Can not inherit MarshalByRefObject class { public void RegUser(User user) { Console.WriteLine($"User registration was successful. Name:{user.Name} Password:{user.Password}"); } } #endregion business #region Characteristic /// <summary> /// Exception handling characteristics /// </summary> public class ExceptionHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new ExceptionHandler() { Order = Order }; } } /// <summary> /// Log Processing Features /// </summary> public class LogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new LogHandler() { Order = Order }; } } /// <summary> /// User Information Features /// </summary> public class UserHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { ICallHandler handler = new UserHandler() { Order = Order }; return handler; } } /// <summary> /// Subsequent Log Features /// </summary> public class AfterLogHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new AfterLogHandler() { Order = Order }; } } #endregion Characteristic #region Behavior corresponding to an attribute public class ExceptionHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); if (methodReturn.Exception == null) { Console.WriteLine("ExceptionHandler:No exceptions"); } else { Console.WriteLine($"ExceptionHandler:An exception occurred:{methodReturn.Exception.Message}"); } return methodReturn; } } public class LogHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { User user = input.Inputs[0] as User; string message = string.Format($"Name:{user.Name} Password:{user.Password}"); Console.WriteLine($"LogHandler:Logged. Message:{message}"); IMethodReturn methodReturn = getNext()(input, getNext); return methodReturn; } } public class UserHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { User user = input.Inputs[0] as User; if (user.Password.Length < 10) { return input.CreateExceptionMethodReturn(new Exception("UserHandler:Password length cannot be less than 10 bits")); } //getNext()(input, getNext): A delegation after delegation means a multiple delegation. IMethodReturn methodReturn = getNext()(input, getNext); return methodReturn; } } public class AfterLogHandler : ICallHandler { public int Order { get; set; } public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn methodReturn = getNext()(input, getNext); Console.WriteLine($"AfterLogHandler:Method Execution Results--{methodReturn.ReturnValue}"); Console.WriteLine("AfterLogHandler:After method execution"); return methodReturn; } } #endregion Behavior corresponding to an attribute /// <summary> /// Run tests /// </summary> public static void Show() { User user = new User() { Name = "Hello", Password = "HelloWorld" }; IUnityContainer container = new UnityContainer(); //Declare a container container.AddNewExtension<Interception>() .RegisterType<IUserProcessor, UserProcessor>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>()); //Explicit Interception IUserProcessor processor = container.Resolve<IUserProcessor>(); processor.RegUser(user); //call } }
3) Call:
static void Main(string[] args) { #region Use EntLib\PIAB Unity Implement dynamic proxy(Non-Configurable) UnityAOP.Show(); Console.Read(); #endregion }
4) The results are as follows:
3.2.4, AOP with Configuration using EntLib\PIAB Unity
1) Continue installing Unity.Configuration, Unity.Interception.Configuration, and Newtonsoft.Json in NuGet.
2) The following classes are established:
Entity.cs (User Entity Class)/// <summary> /// User class /// </summary> public class User { public int Id { get; set; } public string Name { get; set; } public string Password { get; set; } }