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的是实现机制有一定的了解。在本系列的Policy Injection Application Block 设计和实现原理里,我对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,在本系列的Policy Injection Application Block自定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。我们再来看看输出的结果:
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...