Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

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

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

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

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

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

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

二、如何创建Sequential CallHandler Pipeline

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

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

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

三、Sequential CallHandler Pipeline的实现

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

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

对于HandlerPipeline,添加了一个新的Property:Handlers,用于在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中。

该方法将在Ordinal的Invoke中调用:

   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 CallHandler的PIAB

为了验证我们上所做的能否实现我们的目标:让运用到某个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 Application的Main()种调用这个DoSomething()方法:

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

由于CustomHandlerA的Ordinal为2,CustomHandlerB的Ordinal为1,所以他们正确的执行顺序为: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的执行顺序

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-09-07 08:12:19

Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序的相关文章

Enterprise Library Policy Injection Application Block之四

一.为什么CallHandler需要进行排序 PIAB为我们提供了一个很好地实现AOP的方式.AOP旨在实现Business Logic和Non-Business Infrastructure Logic的分离.通过PIAB,我们将这些业务无关的逻辑定义在一个个的CallHandler中,然后通过Attribute或者Configuration的方式,将我们所需的CallHandler运用到相应的目标对象中.从这个意义上讲,PIAB具有很好的Flexibility和Extensibility.但

Enterprise Library Policy Injection Application Block 之一: PIAB Overview

在过去的半年里,定期或者不定期地写点东西已经成为了我的一种习惯.可是最近两个月来一直忙于工作的事情一直足够的时间留给自己,虽然给自己列了很长一串写作计划,可是心有余而力不足.这一段工作主要是帮助公司开发一套分布式的开发框架,对一些技术和设计方法有了一些新的认识.这两天的工作主要是如何把Enterprise Library V3.1的PIAB(Policy Injection Application Block)引入到我们自己的框架中,为次对PIAB进行了一些研究,借此机会与大家一起分享. 一.B

Enterprise Library Policy Injection Application Block 之三:PIAB的扩展—创建自定义CallHandler(提供Source Code下载)

本系列的第一部分对PIAB使用场景进行了简单的介绍,作中阐述了通过PI(Policy Injection)的方式实现了Business Logic和Non-Business Infrastructure Logic的分离,从而实现了AOP(Aspect Oriented Programming).在第二部分中详细介绍PIAB的实现机制:通过自定义RealProxy的方式实现了Method Injection.通过这几天接收到的网友的留言,觉得很多人对在具体的项目开发中如何使用PIAB还有很多困惑

Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理

在前面一篇文章中,我对Enterprise Library中的PIAB (Policy Injection Application Block)作了简单的介绍.在这篇文章主要谈谈我个人对PIAB设计和实现原理的一些理解.在介绍过程中,我尽量采用由浅入深出的方式,同时结合例子.Source Code.希望通过本篇文章让大家对PIAB有一个全面.深刻的认识. 一.MBR.ObjRef.RealProxy.TransparentProxy 在真正进入PIAB之前,我们现来谈论一些与之相关的.必要的背景

WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成

在上一篇文章中,我们通过自定义InstanceProvider实现了WCF和微软Enterprise Library Unity Application Block的集成, 今天我们已相同的方式实现WCF与Enterprise Library的另一个Application Block的集成:Policy Injection Application Block (PIAB). PIAB,通过Method Interception的机制实现了AOP(Aspect Oriented Programin

Enterprise Library Policy Injection Application Block之二

在前面一篇文章中,我对Enterprise Library中的PIAB (Policy Injection Application Block)作了简单的介绍.在这篇文章主要谈谈我个人对PIAB设计和实现原理的一些理解.在介绍过程中,我尽量采用由浅入深出的方式,同时结合例子.Source Code.希望通过本篇文章让大家对PIAB有一个全面.深刻的认识. 一.MBR.ObjRef.RealProxy.TransparentProxy 在真正进入PIAB之前,我们现来谈论一些与之相关的.必要的背景

Enterprise Library Policy Injection Application Block之一

在过去的半年里,定期或者不定期地写点东西已经成为了我的一种习惯.可是最近两个月来一直忙于工作的事情一直足够的时间留给自己,虽然给自己列了很长一串写作计划,可是心有余而力不足.这一段工作主要是帮助公司开发一套分布式的开发框架,对一些技术和设计方法有了一些新的认识.这两天的工作主要是如何把Enterprise Library V3.1的PIAB(Policy Injection Application Block)引入到我们自己的框架中,为次对PIAB进行了一些研究,借此机会与大家一起分享. Par

Enterprise Library Policy Injection Application Block之三

本系列的第一部分对PIAB使用场景进行了简单的介绍,作中阐述了通过PI(Policy Injection)的方式实现了Business Logic和Non-Business Infrastructure Logic的分离,从而实现了AOP(Aspect Oriented Programming).在第二部分中详细介绍PIAB的实现机制:通过自定义RealProxy的方式实现了Method Injection.通过这几天接收到的网友的留言,觉得很多人对在具体的项目开发中如何使用PIAB还有很多困惑

Enterprise Library深入解析与灵活应用(1):通过Unity Extension实现和Policy Injection Application Block的集成

Enterprise Library是微软P&P部门开发的众多Open source框架中的一个,最新的版本已经出到了4.0.由于接触Enterprise Library已经有很长的一段时间,在实际的项目中使用的频率也很高.对此有了一些积累,希望通过这个新的系列和广大网友一起分享和交流.本系列假设读者已经对Enterprise Library有一定的了解,故而不会对各个Application Block的基本原理和编程模型进行介绍,而把侧重点放在Enterprise Library深层次的实现原