Home  >  Article  >  Backend Development  >  C# Advanced Series—AOP? AOP!

C# Advanced Series—AOP? AOP!

黄舟
黄舟Original
2017-02-07 15:40:111451browse

Preface: This article is intended to be about AOP. Speaking of AOP, in fact, the blogger has only been exposed to this concept for a few months. After understanding it, I realized that many of the code principles I wrote before were based on AOP, such as MVC filtering. Filter, the exception capture in it can be processed through FilterAttribute and IExceptionFilter. The internal principle of the processing mechanism of these two objects should be AOP, but there was no such concept before.


1. AOP concept


Old rules, let’s look at the official explanation first: AOP (Aspect- Oriented Programming (Aspect-Oriented Programming), it is a technology that can dynamically and uniformly add functions to the program without modifying the source code through pre-compilation and run-time dynamic agents. It is a new methodology that supplements traditional OOP programming. OOP focuses on dividing required functions into different and relatively independent, well-encapsulated classes, and allowing them to have their own behaviors, relying on inheritance and polymorphism to define their relationships; AOP hopes to be able to transform general required functions from Separating related classes allows many classes to share a behavior. Once a change occurs, there is no need to modify many classes, but only the behavior. AOP uses aspects to modularize cross-cutting concerns, and OOP uses classes to modularize state and behavior. In the OOP world, programs are organized through classes and interfaces, and it is very appropriate to use them to implement the core business logic of the program. However, it is very difficult to implement cross-cutting concerns (functional requirements that span multiple modules of the application), such as logging, permission verification, exception interception, etc.


Blogger’s understanding: AOP extracts common functions. If the requirements for common functions change in the future, you only need to change the code of the common modules. Multiple The calling location does not need to be changed. The so-called aspect-oriented means that we only focus on general functions and not on business logic. The implementation is generally through interception. For example, any of our web projects basically have a permission verification function. Before entering each page, it will verify whether the currently logged in user has permission to view the interface. It is impossible for us to write this paragraph in the initialization method of each page. Verification code, at this time our AOP comes in handy. The mechanism of AOP is to pre-define a set of characteristics so that it has the function of intercepting methods, allowing you to do the business you want to do before and after executing the method, and When we use it, we only need to add a certain feature to the corresponding method or class definition.


2. Advantages of using AOP

The blogger feels that its advantages are mainly reflected in:

1. Separate general functions from business By extracting the logic, a large amount of repeated code can be omitted, which is beneficial to the operation and maintenance of the code.

2. During software design, extracting common functions (aspects) is conducive to the modularization of software design and reduces the complexity of software architecture. In other words, the general functions are a separate module, and the design codes for these general functions cannot be seen in the main business of the project.

3. Simple application of AOP

In order to explain the working principle of AOP, the blogger plans to start with a simple example to understand how AOP works through static interception.

1. Static interception

public class Order
{
    public int Id { set; get; }
    public string Name { set; get; }
    public int Count { set; get; }
    public double Price { set; get; }
    public string Desc { set; get; }
}
 
public interface IOrderProcessor
{
    void Submit(Order order);
}
public class OrderProcessor : IOrderProcessor
{
    public void Submit(Order order)
    {
        Console.WriteLine("提交订单");
    }
}
 
public class OrderProcessorDecorator : IOrderProcessor
{
    public IOrderProcessor OrderProcessor { get; set; }
    public OrderProcessorDecorator(IOrderProcessor orderprocessor)
    {
        OrderProcessor = orderprocessor;
    }
    public void Submit(Order order)
    {
        PreProceed(order);
        OrderProcessor.Submit(order);
        PostProceed(order);
    }
    public void PreProceed(Order order)
    {
        Console.WriteLine("提交订单前,进行订单数据校验....");
        if (order.Price 0)
        {
            Console.WriteLine("订单总价有误,请重新核对订单。");
        }
    }
 
    public void PostProceed(Order order)
    {
        Console.WriteLine("提交带单后,进行订单日志记录......");
        Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "提交订单,订单名称:" + order.Name + ",订单价格:" + order.Price);
    }
}

Calling code:

static void Main(string[] args)
   {
       Order order = new Order() { Id = 1, Name = "lee", Count = 10, Price = 100.00, Desc = "订单测试" };
       IOrderProcessor orderprocessor = new OrderProcessorDecorator(new OrderProcessor());
       orderprocessor.Submit(order);
       Console.ReadLine();
   }

Obtained result:

C# Advanced Series—AOP? AOP!

上面我们模拟订单提交的例子,在提交一个订单前,我们需要做很多的准备工作,比如数据有效性校验等;订单提交完成之后,我们还需要做日志记录等。上面的代码很简单,没有任何复杂的逻辑,从上面的代码可以看出,我们通过静态植入的方式手动在执行方法前和执行方法后让它做一些我们需要的功能。AOP的实现原理应该也是如此,只不过它帮助我们做了方法拦截,帮我们省去了大量重复代码,我们要做的仅仅是写好拦截前和拦截后需要处理的逻辑。


2、动态代理

了解了静态拦截的例子,你是否对AOP有一个初步的认识了呢。下面我们就来到底AOP该如何使用。按照园子里面很多牛人的说法,AOP的实现方式大致可以分为两类:动态代理和IL 编织两种方式。博主也不打算照本宣科,分别拿Demo来说话吧。下面就以两种方式各选一个代表框架来说明。

动态代理方式,博主就以微软企业库(MS Enterprise Library)里面的PIAB(Policy Injection Application Block)框架来作说明。

首先需要下载以下几个dll,然后添加它们的引用。

C# Advanced Series—AOP? AOP!

然后定义对应的Handler

public class User
 {
     public string Name { set; get; }
     public string PassWord { set; get; }
 }
 
 #region 1、定义特性方便使用
 public class LogHandlerAttribute : HandlerAttribute
 {
     public string LogInfo { set; get; }
     public int Order { get; set; }
     public override ICallHandler CreateHandler(IUnityContainer container)
     {
         return new LogHandler() { Order = this.Order, LogInfo = this.LogInfo };
     }
 }
 #endregion
 
 #region 2、注册对需要的Handler拦截请求
 public class LogHandler : ICallHandler
 {
     public int Order { get; set; }
     public string LogInfo { set; get; }
 
     
//这个方法就是拦截的方法,可以规定在执行方法之前和之后的拦截
     public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
     {
         Console.WriteLine("LogInfo内容" + LogInfo);
         
//0.解析参数
         var arrInputs = input.Inputs;
         if (arrInputs.Count > 0)
         {
             var oUserTest1 = arrInputs[0] as User;
         }
         
//1.执行方法之前的拦截
         Console.WriteLine("方法执行前拦截到了");
         
//2.执行方法
         var messagereturn = getNext()(input, getNext);
 
         
//3.执行方法之后的拦截
         Console.WriteLine("方法执行后拦截到了");
         return messagereturn;
     }
 }
 #endregion
 
 #region 3、用户定义接口和实现
 public interface IUserOperation
 {
     void Test(User oUser);
     void Test2(User oUser, User oUser2);
 }
 
 
//这里必须要继承这个类MarshalByRefObject,否则报错
 public class UserOperation : MarshalByRefObject, IUserOperation
 {
     private static UserOperation oUserOpertion = null;
     public UserOperation()
     {
         
//oUserOpertion = PolicyInjection.Create();
     }
 
     
//定义单例模式将PolicyInjection.Create()产生的这个对象传出去,这样就避免了在调用处写这些东西
     public static UserOperation GetInstance()
     {
         if (oUserOpertion == null)
             oUserOpertion = PolicyInjection.Create();
 
         return oUserOpertion;
     }
     
//调用属性也会拦截
     public string Name { set; get; }
 
     
//[LogHandler],在方法上面加这个特性,只对此方法拦截
     [LogHandler(LogInfo = "Test的日志为aaaaa")]
     public void Test(User oUser)
     {
         Console.WriteLine("Test方法执行了");
     }
 
     [LogHandler(LogInfo = "Test2的日志为bbbbb")]
     public void Test2(User oUser, User oUser2)
     {
         Console.WriteLine("Test2方法执行了");
     }
 }
 #endregion

最后我们来看调用的代码:

static void Main(string[] args)
{
    try
    {
        var oUserTest1 = new User() { Name = "test2222", PassWord = "yxj" };
        var oUserTest2 = new User() { Name = "test3333", PassWord = "yxj" };
        var oUser = UserOperation.GetInstance();
        oUser.Test(oUserTest1);
        oUser.Test2(oUserTest1,oUserTest2);
    }
    catch (Exception ex)
    {
        
//throw;
    }
}

得到结果如下:

C# Advanced Series—AOP? AOP!

我们来看执行Test()方法和Test2()方法时候的顺序。

C# Advanced Series—AOP? AOP!

由于Test()和Test2()方法上面加了LogHander特性,这个特性里面定义了AOP的Handler,在执行Test和Test2方法之前和之后都会进入Invoke()方法里面。其实这就是AOP的意义所在,将切面的通用功能在统一的地方处理,在主要逻辑里面直接用过特性使用即可。


3、IL编织

静态织入的方式博主打算使用PostSharp来说明,一来这个使用起来简单,二来项目中用过这种方式。

Postsharp从2.0版本就开始收费了。为了说明AOP的功能,博主下载了一个免费版本的安装包,使用PostSharp与其它框架不太一样的是一定要下载安装包安装,只引用类库是不行的,因为上文说过,AOP框架需要为编译器或运行时添加扩展。使用步骤如下:

(1)下载Postsharp安装包,安装。

(2)在需要使用AOP的项目中添加PostSharp.dll 这个dll的引用。

(3)定义拦截的方法:

[Serializable]
public class TestAop : PostSharp.Aspects.OnMethodBoundaryAspect
{     
//发生异常时进入此方法
    public override void OnException(MethodExecutionArgs args)
    {
        base.OnException(args);
    }
 
//执行方法前执行此方法
    public override void OnEntry(MethodExecutionArgs args)
    {
        base.OnEntry(args);
    }
 
//执行方法后执行此方法
    public override void OnExit(MethodExecutionArgs args)
    {
        base.OnExit(args);
    }
}

注意这里的TestAop这个类必须要是可序列化的,所以要加上[Serializable]特性

(4)在需要拦截功能的地方使用。

在类上面加特性拦截,此类下面的所有的方法都会具有拦截功能。

[TestAop]public class Impc_TM_PLANT : Ifc_TM_PLANT
  {
      /// 
      /// 获取或设置服务接口。
      /// 
      private Ic_TM_PLANTService service { get; set; }
 
      public IList Find()
      {
          DTO_TM_PLANT otest = null;
          otest.NAME_C = "test";
            //异常,会进入OnException方法
      return service.FindAll();   
   }  
}

方法上面加特性拦截,只会拦截此方法。

[TestAop]
public IList Find()
{
    DTO_TM_PLANT otest = null;
    otest.NAME_C = "test";
    return service.FindAll();
}

有没有感觉很简单,很强大,其实这一简单应用,解决我们常见的日志、异常、权限验证等功能简直太小菜一碟了。当然Postsharp可能还有许多更加高级的功能,有兴趣可以深究下。

4、MVC里面的Filter

public class AOPFilterAttribute : ActionFilterAttribute, IExceptionFilter
 {
 
     public void OnException(ExceptionContext filterContext)
     {
         throw new System.NotImplementedException();
     }
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     {
 
         base.OnActionExecuting(filterContext);
     }
 
     public override void OnActionExecuted(ActionExecutedContext filterContext)
     {
         base.OnActionExecuted(filterContext);
     }
 }

在controller里面使用该特性:

[AOPFilter]
   public JsonResult GetEditModel(string strType)
   {
       var lstRes = new List>();
       var lstResPage = new List();        
//.........todo
 
       return Json(new { lstDataAttr = lstRes, PageAttr = lstResPage, lstJsConnections = lstJsPlumbLines }, JsonRequestBehavior.AllowGet);
   }

调试可知,在执行GetEditModel(string strType)方法之前,会先执行OnActionExecuting()方法,GetEditModel(string strType)之后,又会执行OnActionExecuted()方法。这在我们MVC里面权限验证、错误页导向、日志记录等常用功能都可以方便解决。

The above is the C# advanced series-AOP? AOP! For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn