Policy Injection Application Block (PIAB) 提供優雅的 AOP 實作,但原生版本無法控制多個 CallHandler 在同一方法上的執行順序。本文透過修改原始碼實作可排序 CallHandler Pipeline,引入 Ordinal 屬性決定執行優先序。
典型案例:ValidationHandler (參數驗證) → TransactionEnlistHandler (交易登記) → AuditLoggingHandler (稽核記錄)。
PIAB 透過 RealProxy 攔截方法呼叫,建構 HandlerPipeline (CallHandler 串聯鏈)。切入點:Pipeline 建構完成後,依 Ordinal 重新排序。
public class HandlerPipeline
{
private List<ICallHandler> handlers;
public List<ICallHandler> Handlers
{
get { return handlers; }
set { handlers = value; }
}
// ... 原有成員
}
public HandlerPipeline ResortHandlers(HandlerPipeline pipeline)
{
var sequentialPipeline = new HandlerPipeline();
var handlerOrdinalPairList = new Dictionary<ICallHandler, int>();
var handlers = pipeline.Handlers.ToArray();
var ordinals = new int[handlers.Length];
for (int i = 0; i < handlers.Length; i++)
{
var handler = handlers[i];
var memberInfos = handler.GetType().GetMember("Ordinal");
if (memberInfos.Length == 0) { ordinals[i] = int.MaxValue; continue; }
var prop = memberInfos[0] as PropertyInfo;
if (prop == null) { ordinals[i] = int.MaxValue; continue; }
ordinals[i] = (int)prop.GetValue(handler, null);
}
// 氣泡排序 (簡單示範,實務可用 LINQ OrderBy)
for (int i = 0; i < handlers.Length - 1; i++)
for (int j = i + 1; j < handlers.Length; j++)
if (ordinals[i] > ordinals[j])
{
Swap(ref handlers[i], ref handlers[j]);
Swap(ref ordinals[i], ref ordinals[j]);
}
return new HandlerPipeline(handlers);
}
public override IMessage Invoke(IMessage msg)
{
// ... 取得 pipeline ...
pipeline = ResortHandlers(pipeline); // 關鍵:排序後再執行
// ... 執行 pipeline ...
}
建議:定義抽象基底類別 OrderedCallHandlerBase 內建 Ordinal 屬性,避免反射效能損耗。
public class CustomHandlerA : ICallHandler
{
public int Ordinal { get; set; } = int.MaxValue;
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("CustomHandlerA invoked!");
return getNext()(input, getNext);
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ACustomHandlerAttribute : HandlerAttribute
{
public int Ordinal { get; set; }
public override ICallHandler CreateHandler()
=> new CustomHandlerA { Ordinal = this.Ordinal };
}
// CustomHandlerB / BCustomHandlerAttribute 同理
public class PolicyInjectionType : MarshalByRefObject
{
[BCustomHandlerAttribute(Ordinal = 1)] // 先執行
[ACustomHandlerAttribute(Ordinal = 2)] // 後執行
public void DoSomething()
{
Console.WriteLine("Target method invoked!");
}
}

調換 Ordinal 值:
[BCustomHandlerAttribute(Ordinal = 2)]
[ACustomHandlerAttribute(Ordinal = 1)]

輸出證實順序受 Ordinal 控制,解決原生 PIAB 無法排序的缺憾。