WCF 擁有極具彈性的擴展架構,其中行為擴展是最常見且強大的切入點。本文系統梳理伺服器端與客戶端擴展點、四種行為類型、配置與屬性兩種啟用方式,並提供完整程式碼範例。
| 層面 | 運行階段 | 擴展介面 | 用途 |
|---|---|---|---|
| 伺服器端 | DispatchRuntime | IParameterInspector | 參數檢查(呼叫前/後) |
| DispatchRuntime | IDispatchMessageInspector | 訊息檢查(接收請求/發送回覆) | |
| DispatchOperation | IOperationInvoker | 自訂操作調用邏輯 | |
| 客戶端 | ClientRuntime | IParameterInspector | 參數檢查(共用介面) |
| ClientRuntime | IClientMessageInspector | 訊息檢查(發送請求/接收回覆) |
所有介面定義於 System.ServiceModel.Dispatcher 命名空間。IParameterInspector 可同時用於雙端。
public class CalculatorParameterInspector : IParameterInspector {
public object BeforeCall(string operationName, object[] inputs) {
int x = (int)inputs[0], y = (int)inputs[1];
if (x < 0 || y < 0)
throw new FaultException("The number can not be less than zero.");
return null; // 相關狀態傳給 AfterCall
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) {
// 可記錄回傳值、效能監控等
}
}
public class PrintMessageInterceptor : IDispatchMessageInspector {
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
var buffer = request.CreateBufferedCopy(int.MaxValue);
request = buffer.CreateMessage();
Console.WriteLine("After Receive Request:");
foreach (var header in request.Headers) Console.WriteLine(header);
Console.WriteLine(new string('*', 20));
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState) {
var buffer = reply.CreateBufferedCopy(int.MaxValue);
reply = buffer.CreateMessage();
Console.WriteLine("Before Send Reply:");
foreach (var header in reply.Headers) Console.WriteLine(header);
Console.WriteLine(new string('*', 20));
}
}
注意:IDispatchMessageInspector(伺服端)方法順序為 AfterReceiveRequest → BeforeSendReply;IClientMessageInspector(客戶端)則相反:BeforeSendRequest → AfterReceiveReply。
| 行為類型 | 介面 | 服務 | 端點 | 合約 | 操作 |
|---|---|---|---|---|---|
| 服務行為 | IServiceBehavior | ✓ | ✓ | ✓ | ✓ |
| 端點行為 | IEndpointBehavior | ✓ | ✓ | ✓ | |
| 合約行為 | IContractBehavior | ✓ | ✓ | ||
| 操作行為 | IOperationBehavior | ✓ |
| 方式 | 服務行為 | 端點行為 | 合約行為 | 操作行為 |
|---|---|---|---|---|
| 自訂屬性 | ✓ | ✗ | ✓ | ✓ |
| 配置檔 | ✓ | ✓ | ✗ | ✗ |
| ServiceDescription 程式碼 | ✓ | ✓ | ✓ | ✓ |
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class MyServiceBehavior : Attribute, IServiceBehavior {
// 實作 IServiceBehavior 三個方法...
}
[MyServiceBehavior]
public interface IService { ... }
public class MyBehaviorExtensionElement : BehaviorExtensionElement {
public override Type BehaviorType => typeof(MyServiceBehavior);
protected override object CreateBehavior() => new MyServiceBehavior();
[ConfigurationProperty("providerName", IsRequired = true)]
public string ProviderName {
get => (string)this["providerName"];
set => this["providerName"] = value;
}
}
配置檔:
<system.serviceModel>
<services>
<service name="Demo.Calculator">
<endpoint behaviorConfiguration="messageInspectorBehavior" ... />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="messageInspectorBehavior">
<myBehaviorExtensionElement providerName="Test" />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="myBehaviorExtensionElement"
type="Demo.MyBehaviorExtensionElement, Demo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
關鍵細節:type 必須為完整型別名稱(含命名空間、版本、文化特性、公開金鑰),逗號後必須保留一空格,否則反射載入失敗。
public class CalculatorParameterValidation : Attribute, IOperationBehavior {
public void ApplyClientBehavior(OperationDescription desc, ClientOperation op) {
op.ParameterInspectors.Add(new CalculatorParameterInspector());
}
public void ApplyDispatchBehavior(OperationDescription desc, DispatchOperation op) {
op.ParameterInspectors.Add(new CalculatorParameterInspector());
}
// AddBindingParameters, Validate 留空
}
public class CalculatorParameterValidation : Attribute, IParameterInspector, IOperationBehavior {
// 實作 IParameterInspector 方法...
public void ApplyClientBehavior(OperationDescription desc, ClientOperation op) {
op.ParameterInspectors.Add(this); // this 即為參數檢查器
}
public void ApplyDispatchBehavior(OperationDescription desc, DispatchOperation op) {
op.ParameterInspectors.Add(this);
}
}
public class MyOperationInvokerBehavior : Attribute, IOperationBehavior {
public void ApplyDispatchBehavior(OperationDescription desc, DispatchOperation op) {
op.Invoker = new MyOperationInvoker(op.Invoker); // 裝飾模式包裝原調用器
}
}
public class PrintMessageInspectorBehavior : IDispatchMessageInspector, IEndpointBehavior {
// 實作 IDispatchMessageInspector...
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher dispatcher) {
dispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
}
若實作 IServiceBehavior,需遍歷 ServiceHostBase.ChannelDispatchers:
public void ApplyDispatchBehavior(ServiceDescription desc, ServiceHostBase host) {
foreach (var chDisp in host.ChannelDispatchers)
foreach (var epDisp in chDisp.Endpoints)
epDisp.DispatchRuntime.MessageInspectors.Add(this);
}
type 字串格式嚴格,逗號後空格不可省略