程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Enterprise Library Policy Injection Application Block之四

Enterprise Library Policy Injection Application Block之四

編輯:關於.NET

一、為什麼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以利於排序。

public class HandlerPipeline
  {
    private List<ICallHandler> handlers;
    ///<summary>
    /// This property is added to get the CallHandler(s).
    ///</summary>
    public List<ICallHandler> Handlers
    {
      get { return handlers; }
      set { handlers = value; }
    }
   。。。。。。
}

在才中,添加一個新的方法:ResortHandlers,將所有CallHandler按照Ordinal的大小進行重新排序(通過Reflection得到Ordinal的值)。

public HandlerPipeline ResortHandlers(HandlerPipeline pipeline)
    {
      HandlerPipeline sequentialPipeline = new HandlerPipeline();
      IDictionary<ICallHandler,int> handlerOrdinalPairList = new Dictionary<ICallHandler,int>();
      ICallHandler[] handlers = Array.CreateInstance(typeof(ICallHandler), pipeline.Handlers.Count) as ICallHandler[];
      int[] ordinals = Array.CreateInstance(typeof(int), pipeline.Handlers.Count) as int[];
      for (int i = 0; i < pipeline.Handlers.Count; i++ )
      {
        ICallHandler handler = pipeline.Handlers[i];
        handlers[i] = handler;
        Type handlerType = handler.GetType();
        MemberInfo[] memberInfos = handlerType.GetMember("Ordinal");
        if (memberInfos.Length == 0)
        {
          ordinals[i] = int.MaxValue;
          continue;
        }
        PropertyInfo propertyInfo = memberInfos[0] as PropertyInfo;
        if (propertyInfo == null)
        {
          ordinals[i] = int.MaxValue;
          continue;
        }
        int ordinal = (int)propertyInfo.GetValue(handler, null);
        ordinals[i] = ordinal;
      }
      ICallHandler swapHandler;
      int swapOrdinal;
      for (int i = 0; i < pipeline.Handlers.Count - 1; i++)
      {
        for (int j = i + 1; j < pipeline.Handlers.Count; j++)
        {
          if (ordinals[i] > ordinals[j])
          {
            swapOrdinal = ordinals[i];
            ordinals[i] = ordinals[j];
            ordinals[j] = swapOrdinal;
            swapHandler = handlers[i];
            handlers[i] = handlers[j];
            handlers[j] = swapHandler;
          }
        }
      }
      return new HandlerPipeline(handlers);
    }

注:采用Reflection的方式獲得Ordinal並不是一種很好的方式,最好是定義一個Abstract CallHandler BaseClass,並將Ordinal Property定義在這個BaseClass中。

該方法將在Ordinal的Invoke中調用:

public override IMessage Invoke(IMessage msg)
    {
      IMethodCallMessage callMessage = (IMethodCallMessage)msg;
      HandlerPipeline pipeline;
      if (memberHandlers.ContainsKey(callMessage.MethodBase))
      {
        pipeline = memberHandlers[callMessage.MethodBase];
        //Added by Jiang Jin Nan
        pipeline = ResortHandlers(pipeline);
      }
      else
      {
        pipeline = new HandlerPipeline();
      }
      RemotingMethodInvocation invocation = new RemotingMethodInvocation(callMessage, target);
      IMethodReturn result =
        pipeline.Invoke(
          invocation,
          delegate(IMethodInvocation input, GetNextHandlerDelegate getNext)
          {
            try
            {
              object returnValue = callMessage.MethodBase.Invoke(target, invocation.Arguments);
              return input.CreateMethodReturn(returnValue, invocation.Arguments);
            }
            catch (TargetInvocationException ex)
            {
              // The outer exception will always be a reflection exception; we want the inner, which is
              // the underlying exception.
              return input.CreateExceptionMethodReturn(ex.InnerException);
            }
          });
      return ((RemotingMethodReturn)result).ToMethodReturnMessage();
    }

這就是所有需要的改動,為了驗證是否有效,我們照例寫一個測試程序。

四、如何使用Sequential CallHandler的PIAB

為了驗證我們上所做的能否實現我們的目標:讓運用到某個Method上的CallHandler按照我們希望的順序來執行,我們創建了兩個Custom CallHandler: CustomHandlerA 和CustomHandlerB:

namespace Artech.SequentialCallHandlers
{
  public class CustomHandlerA: ICallHandler
  {
    public int Ordinal
    { get; set; }
    public CustomHandlerA()
    {
      this.Ordinal = int.MaxValue;
    }
    #region ICallHandler Members
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
      Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerA is invoked!");
      return getNext()(input, getNext);
    }
    #endregion
  }
}
namespace Artech.SequentialCallHandlers
{
  public class CustomHandlerB:ICallHandler
  {
    public int Ordinal
    { get; set; }
    public CustomHandlerB()
    {
      this.Ordinal = int.MaxValue;
    }
    #region ICallHandler Members
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
      Console.WriteLine("Artech.SequentialCallHandlers.CustomHandlerB is invoked!");
      return getNext()(input, getNext);
    }
    #endregion
  }
}

下面是兩個對應的HandlerAttribute:

namespace Artech.SequentialCallHandlers
{
  public class ACustomHandlerAttribute:HandlerAttribute
  {
    public int Ordinal
    { get; set; }
    public override ICallHandler CreateHandler()
    {
      return new CustomHandlerA() { Ordinal = this.Ordinal };
    }
  }
}
namespace Artech.SequentialCallHandlers
{
  [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
  public class BCustomHandlerAttribute:HandlerAttribute
  {
    public int Ordinal
    { get; set; }
    public override ICallHandler CreateHandler()
    {
      return new CustomHandlerB() { Ordinal = this.Ordinal };
    }
  }
}

注:如何定義Custom CallHandler,在本系列的第三部分有詳細的介紹。

然後,我們將這連個Attribute運用到同一個方法中:

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

我們在一個Console Application的Main()種調用這個DoSomething()方法:

class Program
  {
    static void Main(string[] args)
    {
      PolicyInjectionType proxy = PolicyInjection.Create<PolicyInjectionType>();
      proxy.DoSomething();
    }
}

由於CustomHandlerA的Ordinal為2,CustomHandlerB的Ordinal為1,所以他們正確的執行順序為:CustomHandlerB-〉CustomHandlerA。輸出的結果證實了這一點:

我們來改變一下他們的順序:

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

這樣的話,兩個CallHandler的順序將變成:CustomHandlerA-〉CustomHandlerB。我們再來看看輸出的結果:

這正是我們所希望的。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved