本文以「訂單驗證」為例,完整示範自訂 CallHandler 開發全流程:建立驗證邏輯、屬性模式套用、組態模式套用 (需 HandlerData + CallHandlerAssembler)、映射關係建立。

Artech.CustomCallHandlers.ClassLibrary:自訂 CallHandler、屬性、組態類別Artech.CustomCallHandlers.ConsoleApp:測試專案EnterpriseLibrary.Common、PolicyInjection、ObjectBuilderpublic class Order
{
public Guid OrderNo { get; set; }
public DateTime OrderDate { get; set; }
public string Supplier { get; set; }
public IList<OrderItem> Items { get; set; } = new List<OrderItem>();
public double TotalPrice { get; set; }
}
public class OrderItem
{
public Guid ProductID { get; set; }
public string ProductName { get; set; }
public double UnitPrice { get; set; }
public int Quantity { get; set; }
}
public class OrderValidationException : ApplicationException
{
public OrderValidationException(string msg) : base(msg) { }
}
[ConfigurationElementType(typeof(OrderValidationCallHandlerData))]
public class OrderValidationCallHandler : ICallHandler
{
public static IList<string> LegalSuppliers { get; } = new List<string> { "Company AAA", "BBB", "CCC" };
public bool ValidateTotalPrice { get; set; }
public bool ValidateSupplier { get; set; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (input.Inputs.Count == 0) return getNext()(input, getNext);
var order = input.Inputs[0] as Order;
if (order == null) return getNext()(input, getNext);
// 必須驗證
if (order.OrderDate > DateTime.Today)
return input.CreateExceptionMethodReturn(new OrderValidationException("Order date > today"));
if (order.Items.Count == 0)
return input.CreateExceptionMethodReturn(new OrderValidationException("No items"));
// 可選驗證
if (ValidateSupplier && !LegalSuppliers.Contains(order.Supplier))
return input.CreateExceptionMethodReturn(new OrderValidationException("Illegal supplier"));
if (ValidateTotalPrice)
{
double sum = order.Items.Sum(i => i.Quantity * i.UnitPrice);
if (sum != order.TotalPrice)
return input.CreateExceptionMethodReturn(new OrderValidationException("Price mismatch"));
}
return getNext()(input, getNext); // 通過驗證,繼續管線
}
}
關鍵點:CreateExceptionMethodReturn 短路管線拋出異常;getNext()(input, getNext) 傳遞給下一個 Handler 或目標方法。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class OrderValidationCallHandlerAttribute : HandlerAttribute
{
public bool ValidateTotalPrice { get; set; }
public bool ValidateSupplier { get; set; }
public override ICallHandler CreateHandler()
=> new OrderValidationCallHandler { ValidateTotalPrice = this.ValidateTotalPrice, ValidateSupplier = this.ValidateSupplier };
}
public class OrderProcessor : MarshalByRefObject
{
[OrderValidationCallHandlerAttribute] // 預設不啟用可選驗證
public void ProcessOrder(Order order) => Console.WriteLine("Order processed!");
public static Order CreateOrder(DateTime date, string supplier)
=> new Order { OrderNo = Guid.NewGuid(), OrderDate = date, Supplier = supplier, TotalPrice = 10000,
Items = { new OrderItem { ProductName = "PC", UnitPrice = 6000, Quantity = 1 },
new OrderItem { ProductName = "Print", UnitPrice = 5000, Quantity = 2 } } };
}
class Program
{
static void Main()
{
var proxy = PolicyInjection.Create<OrderProcessor>();
// 測試三種失敗案例...
}
}

加上屬性參數啟用可選驗證:
[OrderValidationCallHandlerAttribute(ValidateSupplier = true, ValidateTotalPrice = true)]

[Assembler(typeof(OrderValidationCallHandlerAssembler))]
public class OrderValidationCallHandlerData : CallHandlerData
{
[ConfigurationProperty("validateSupplier", DefaultValue = false)]
public bool ValidateSupplier { get => (bool)base["validateSupplier"]; set => base["validateSupplier"] = value; }
[ConfigurationProperty("validateTotalPrice", DefaultValue = false)]
public bool ValidateTotalPrice { get => (bool)base["validateTotalPrice"]; set => base["validateTotalPrice"] = value; }
}
public class OrderValidationCallHandlerAssembler : IAssembler<ICallHandler, CallHandlerData>
{
public ICallHandler Assemble(IBuilderContext context, CallHandlerData config, IConfigurationSource source, ConfigurationReflectionCache cache)
{
var data = config as OrderValidationCallHandlerData;
return new OrderValidationCallHandler { ValidateSupplier = data.ValidateSupplier, ValidateTotalPrice = data.ValidateTotalPrice };
}
}
<policyInjection>
<policies>
<add name="Policy">
<matchingRules>
<add type="...MemberNameMatchingRule" name="Member Name Matching Rule">
<matches><add match="ProcessOrder" /></matches>
</add>
</matchingRules>
<handlers>
<add type="Artech.CustomCallHandlers.OrderValidationCallHandler, Artech.CustomCallHandlers"
name="OrderValidationCallHandler" validateSupplier="true" validateTotalPrice="true" />
</handlers>
</add>
</policies>
</policyInjection>
matchingRules:使用 MemberNameMatchingRule 比對方法名 ProcessOrderhandlers:直接指定型別與屬性值移除屬性、套用組態後執行,結果與屬性模式完全一致。