zoukankan      html  css  js  c++  java
  • Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

    一、为什么CallHandler需要进行排序

    PIAB为我们提供了一个很好地实现AOP的方式。AOP旨在实现Business LogicNon-Business Infrastructure Logic的分离。通过PIAB,我们将这些业务无关的逻辑定义在一个个的CallHandler中,然后通过Attribute或者Configuration的方式,将我们所需的CallHandler运用到相应的目标对象中。从这个意义上讲,PIAB具有很好的FlexibilityExtensibility。但是,就我看来PIAB也具有一些不足之处,其最大的局限性在于:不能控制运用到某个Method的多个方法的执行顺序。而让CallHandler按照我们希望的顺序进行调用是非常有必要的。

    举个例子,假设我们将以下3CallHandler运用到某个方法中:

    • ValidationHandler:用于参数参数的验证,比如是否为null, stringLength是否超出长度等等。
    • TransactionEnlistHandler: 用于将操作自动纳入到一个Transaction中,从而保证数据的一致性。
    • AuditLoggingHandler:当时操作成功执行后进行Audit Log

    很显然,正常的执行顺序应该是这样的:在最开始调用ValidationHandler进行参数的验证;Audit Log需要和目标方法一起纳入同一个Transaction中,所以TransactionEnlistHandler的调用紧随其后,最后才是AuditLoggingHandler

    Microsoft提供的原生的PIAB是无法实现的,好在Enterprise Library是开源的,我们可以修改PIABSource Code来使其实现我们的目标。而仅仅是一个很小的改动。接下来我们就来讨论一下如何来实现可被排序的CallHandler Pipeline

    二、如何创建Sequential CallHandler Pipeline

    如果要了解我们这个Sequential CallHandler Pipeline的实现,需要对PIAB的是实现机制有一定的了解。在本系列的第二部分里,我对PIAB的实现机制进行了详细的阐述,在这里我仅仅简单介绍一个PIAB是如何实现AOP的。

    PIABAOP的实现原理可以用一个词来概括:Method Interception。具体的做法做法是:通过PIAB Factory创建基于Target TypeReal Proxy,然后通过这个Real Proxy创建Transparent Proxy,并通过该Transparent Proxy调用Target Instance。在创建Real Proxy中,将运用到该Type的所有CallHandler缓存起来。当进行调用的时候,Transparent Proxy调用Real ProxyInvoke方法。在该方法中,在将运用到当前MethodCallHandler构成一个Handler Pipeline。在真正调用Target Instance之前,按照Pipeline的先后顺序依次调用每个CallHandler

    而我们实现的切入点就是:CallHandler Pipeline创建之后,再根据我们希望的顺序将所有的CallHander重新排序

    三、Sequential CallHandler Pipeline的实现

    实现一个Sequential CallHandler Pipeline的一个前提就是,如何确定一个CallHandlerPipeline的位置。为此,我们需要我们的Custom CallHandler有一个额外的属性:Ordinal,表明在Pipeline的序号,序号小的在前,大的在后。如何没有该属性,比如PIAB提供的所有CallHandler,我们将其放在最后。

    我们仅仅需要修改两个PIAB Class: Microsoft.Practices.EnterpriseLibrary.PolicyInjection. HandlerPipelineMicrosoft.Practices.EnterpriseLibrary.PolicyInjection. RemotingInterception. InterceptingRealProxy

    对于HandlerPipeline,添加了一个新的PropertyHandlers,用于在InterceptingRealProxy中能够获得组成Pipeline的所有CallHandler以利于排序。

       1: public class HandlerPipeline
       2: {
       3:  
       4:     private List<ICallHandler> handlers;
       5:     public List<ICallHandler> Handlers
       6:     {
       7:         get { return handlers; }
       8:         set { handlers = value; }
       9:     }
      10: }

    现在我们添加一个新的方法:ResortHandlers,将所有CallHandler按照Ordinal的大小进行重新排序(通过Reflection得到Ordinal的值)。

       1: public HandlerPipeline ResortHandlers(HandlerPipeline pipeline)
       2: {
       3:     HandlerPipeline sequentialPipeline = new HandlerPipeline();
       4:     IDictionary<ICallHandler, int> handlerOrdinalPairList = new Dictionary<ICallHandler, int>();
       5:     ICallHandler[] handlers = Array.CreateInstance(typeof(ICallHandler), pipeline.Handlers.Count) as ICallHandler[];
       6:     int[] ordinals = Array.CreateInstance(typeof(int), pipeline.Handlers.Count) as int[];
       7:     for (int i = 0; i < pipeline.Handlers.Count; i++)
       8:     {
       9:         ICallHandler handler = pipeline.Handlers[i];
      10:         handlers[i] = handler;
      11:         Type handlerType = handler.GetType();
      12:         MemberInfo[] memberInfos = handlerType.GetMember("Ordinal");
      13:         if (memberInfos.Length == 0)
      14:         {
      15:             ordinals[i] = int.MaxValue;
      16:             continue;
      17:         }
      18:         PropertyInfo propertyInfo = memberInfos[0] as PropertyInfo;
      19:         if (propertyInfo == null)
      20:         {
      21:             ordinals[i] = int.MaxValue;
      22:             continue;
      23:         }
      24:  
      25:         int ordinal = (int)propertyInfo.GetValue(handler, null);
      26:         ordinals[i] = ordinal;
      27:     }
      28:  
      29:     ICallHandler swapHandler;
      30:     int swapOrdinal;
      31:     for (int i = 0; i < pipeline.Handlers.Count - 1; i++)
      32:     {
      33:         for (int j = i + 1; j < pipeline.Handlers.Count; j++)
      34:         {
      35:  
      36:             if (ordinals[i] > ordinals[j])
      37:             {
      38:                 swapOrdinal = ordinals[i];
      39:                 ordinals[i] = ordinals[j];
      40:                 ordinals[j] = swapOrdinal;
      41:                 swapHandler = handlers[i];
      42:                 handlers[i] = handlers[j];
      43:                 handlers[j] = swapHandler;
      44:             }
      45:         }
      46:     }
      47:     return new HandlerPipeline(handlers);
      48: }

    注:采用Reflection的方式获得Ordinal并不是一种很好的方式,最好是定义一个Abstract CallHandler BaseClass,并将Ordinal Property定义在这个BaseClass中。

    该方法将在OrdinalInvoke中调用:

       1: public override IMessage Invoke(IMessage msg)
       2: {
       3:     IMethodCallMessage callMessage = (IMethodCallMessage)msg;
       4:     HandlerPipeline pipeline;
       5:     if (memberHandlers.ContainsKey(callMessage.MethodBase))
       6:     {
       7:         pipeline = memberHandlers[callMessage.MethodBase];
       8:         //Added by Jiang Jin Nan
       9:         pipeline = ResortHandlers(pipeline);
      10:     }
      11:     else
      12:     {
      13:         pipeline = new HandlerPipeline();
      14:     }
      15:  
      16:     RemotingMethodInvocation invocation = new RemotingMethodInvocation(callMessage, target);
      17:     IMethodReturn result =
      18:         pipeline.Invoke(invocation, delegate(IMethodInvocation input, GetNextHandlerDelegate getNext)
      19:             {
      20:                 try
      21:                 {
      22:                     object returnValue = callMessage.MethodBase.Invoke(target, invocation.Arguments);
      23:                     return input.CreateMethodReturn(returnValue, invocation.Arguments);
      24:                 }
      25:                 catch (TargetInvocationException ex)
      26:                 {
      27:                     return input.CreateExceptionMethodReturn(ex.InnerException);
      28:                 }
      29:  
      30:             });
      31:  
      32:     return ((RemotingMethodReturn)result).ToMethodReturnMessage();
      33: }

    这就是所有需要的改动,为了验证是否有效,我们照例写一个测试程序。

    四、如何使用Sequential CallHandlerPIAB

    为了验证我们上所做的能否实现我们的目标:让运用到某个Method上的CallHandler按照我们希望的顺序来执行,我们创建了两个Custom CallHandler: CustomHandlerA CustomHandlerB

       1: namespace Artech.SequentialCallHandlers
       2: {
       3:     public class CustomHandlerA : ICallHandler   
       4:     {
       5:         public int Ordinal{ get; set; }
       6:         public CustomHandlerA()
       7:         {
       8:             this.Ordinal = int.MaxValue;
       9:         }
      10:         public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
      11:         {
      12:             Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerA is invoked!");
      13:             return getNext()(input, getNext);
      14:         }
      15:     }
      16: }

       1: namespace Artech.SequentialCallHandlers
       2: {
       3:     public class CustomHandlerB : ICallHandler
       4:     {
       5:         public int Ordinal{ get; set; }
       6:         public CustomHandlerB()
       7:         {
       8:             this.Ordinal = int.MaxValue;
       9:         }
      10:  
      11:         public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
      12:         {
      13:             Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerB is invoked!");
      14:             return getNext()(input, getNext);
      15:         }
      16:     }
      17: }

     下面是两个对应的HandlerAttribute

       1: namespace Artech.SequentialCallHandlers
       2: {
       3:     public class ACustomHandlerAttribute : HandlerAttribute
       4:     {
       5:         public int Ordinal{ get; set; }
       6:         public override ICallHandler CreateHandler()
       7:         {
       8:             return new CustomHandlerA() { Ordinal = this.Ordinal };
       9:         }
      10:     }
      11: }

       1: namespace Artech.SequentialCallHandlers
       2: {
       3:     [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
       4:     public class BCustomHandlerAttribute : HandlerAttribute
       5:     {
       6:         public int Ordinal{ get; set; }
       7:         public override ICallHandler CreateHandler()
       8:         {
       9:             return new CustomHandlerB() { Ordinal = this.Ordinal };
      10:         }
      11:     }
      12: }

    注:如何定义Custom CallHandler,在本系列的第三部分有详细的介绍。

    然后,我们将这连个Attribute运用到同一个方法中:

       1: class PolicyInjectionType : MarshalByRefObject
       2: {
       3:     [BCustomHandlerAttribute(Ordinal = 1)]
       4:     [ACustomHandlerAttribute(Ordinal = 2)]
       5:     public void DoSomething()
       6:     {
       7:         Console.WriteLine("The target object is invoked!");
       8:     }
       9: }

    我们在一个Console ApplicationMain()种调用这个DoSomething()方法:

       1: class Program
       2: {
       3:     static void Main(string[] args)
       4:     {
       5:         PolicyInjectionType proxy = PolicyInjection.Create<PolicyInjectionType>();
       6:         proxy.DoSomething();
       7:     }
       8: }

    由于CustomHandlerAOrdinal2CustomHandlerBOrdinal1,所以他们正确的执行顺序为:CustomHandlerB-CustomHandlerA。输出的结果证实了这一点:


    我们来改变一下他们的顺序:

       1: class PolicyInjectionType : MarshalByRefObject
       2: {
       3:     [BCustomHandlerAttribute(Ordinal = 2)]
       4:     [ACustomHandlerAttribute(Ordinal = 1)]
       5:     public void DoSomething()
       6:     {
       7:         Console.WriteLine("The target object is invoked!");
       8:     }
       9: }

    这样的话,两个CallHandler的顺序将变成:CustomHandlerA-CustomHandlerB。我们再来看看输出的结果:

    EnterLib PIAB系列:

    Enterprise Library Policy Injection Application Block 之一: PIAB Overview
    Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
    Enterprise Library Policy Injection Application Block 之三: PIAB的扩展—创建自定义CallHandler(提供Source Code下载)
    Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

  • 相关阅读:
    JS高级
    函数作用域面试题
    11.14
    11.13
    Redux知识
    react-router-dom
    react 的三大属性
    vuex
    数组的扩展
    函数作用域和 class
  • 原文地址:https://www.cnblogs.com/artech/p/1076003.html
Copyright © 2011-2022 走看看