程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 詳解WCF可擴展框架中的行為擴展

詳解WCF可擴展框架中的行為擴展

編輯:關於.NET

WCF以其靈活的可擴展架構為開發者提供了方便,其中對行為的擴展或許是應用中最為常見的。自定義 對行為的擴展並不復雜,但仍有許多細節需要注意。

在服務端,一般是對DispatchRuntime和DispatchOperation進行擴展,擴展點包括了對參數和消息的 檢查,以及操作調用程序, 它們對應的接口分別為IParameterInspector,IDispatchMessageInspector 以及 IOperationInvoker。而在客戶端,則是對ClientRuntime和ClientOperation進行擴展,擴展點包括 對參數和消息 的檢查,對應的接口分別為IParameterInspector和IClientMessageInspector。這些接口 類型均被定義在 System.ServiceModel.Dispatcher命名空間下,其中IParameterInspector接口可以同時 作用在服務端和客戶端。

對這些接口的實現,有點類似於AOP的實現,可以對方法調用前和調用後注入一些額外的邏輯,所以通 常會將這些擴展稱為偵聽器。例如IParameterInspector接口,就定義了如下方法:

void AfterCall(string operationName, object[] outputs,

object returnValue, object correlationState);
object BeforeCall(string operationName, object[] inputs);

在調用服務對象的目標方法前,會調用BeforeCall方法,而在調用後則會調用AfterCall方法。例如我 們可在方法調用前檢驗計算方法的參數是否小於0,如果小於0則拋出異常:

public class CalculatorParameterInspector:IParameterInspector
{
public void BeforeCall(string operationName, object[] inputs)
{
int x = inputs[0] as int;
int y = inputs[1] as int;
if (x <0 || y < 0)
{
throw new FaultException("The number can not be less than zero.");
}
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue,

object correlationState)
{
//empty;
}
}

對消息的檢查區分了服務端和客戶端,接口方法根據消息傳遞的順序剛好相反[注]。我們可以通過接 口方法對消息進行處理,例如打印消息的Header:

public class PrintMessageInterceptor : IDispatchMessageInspector
{
#region IDispatchMessageInspector Members

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,  IClientChannel channel, InstanceContext instanceContext)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();

Console.WriteLine("After Receive Request:");
foreach (MessageHeader header in request.Headers)
{
Console.WriteLine(header);
}
Console.WriteLine(new string('*', 20));
return null;
}

public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object  correlationState)
{
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();

Console.WriteLine("Before Send Request:");
foreach (MessageHeader header in reply.Headers)
{
Console.WriteLine(header);
}
Console.WriteLine(new string('*', 20));
}

#endregion
} 

WCF提供了四種類型的行為:服務行為、終結點行為、契約行為和操作行為。 這四種行為分別定義了 四個接口:IServiceBehavior,IEndpointBehavior,IContractBehavior以及 IOperationBehavior。雖 然是四個不同的接口,但它們的接口方法卻基本相同,分別為 AddBindingParameters(), ApplyClientBehavior()以及ApplyDispatchBehavior()。注 意,IServiceBehavior由於只能作用在服務 端,因此並不包含ApplyClientBehavior()方法。

我們可以定義自己的類實現這些接口,但需要注意幾點:

1、行為的作用范圍,可以用如下表格表示:

2、可以利用自定義特性的方式添加擴展的服務行為、契約行為和操作行為,但不能添加終結點行為; 可以利用配置文件添加擴展服務行為和終結點行為,但不能添加契約行為和操作行為。但這些擴展的行為 都可以通過ServiceDescription添加。

利用特性添加行為,意味著我們在定義自己的擴展行為時,可以將其派生自Attribute類,然後以特性 方式添加。例如:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
public class MyServiceBehavior:Attribute, IServiceBehavior...
[MyServiceBehavior]
public interface IService...

如果以配置文件的方式添加行為,則必須定義一個類繼承自BehaviorExtensionElement(屬於命名空 間System.ServiceModel.Configuration),然後重寫屬性BehaviorType以及 CreateBehavior()方法。 BehaviorType屬性返回的是擴展行為的類型,而CreateBehavior()方法則負責創建該擴展 行為的對象實 例:

public class MyBehaviorExtensionElement:BehaviorExtensionElement
{
public MyBehaviorExtensionElement() { }
public override Type BehaviorType
{
get { return typeof(MyServiceBehavior); }
}

protected override object CreateBehavior()
{
return new MyServiceBehavior();
}
}

如果配置的Element添加了新的屬性,則需要為新增的屬性應用ConfigurationPropertyAttribute,例 如:

[ConfigurationProperty("providerName",IsRequired = true)]
public virtual string ProviderName
{
get
{
return this["ProviderName"] as string;
}
set
{
this["ProviderName"] = value;
}
}

配置文件中的配置方法如下所示:

address="http://localhost:801/Calculator"
binding="basicHttpBinding" 
contract="MessageInspectorDemo.ICalculator"/>
service>
services>
behavior>
serviceBehaviors>
behaviors>
type="MessageInspectorDemo.MyBehaviorExtensionElement, MessageInspectorDemo,  Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
behaviorExtensions>
extensions>
system.serviceModel>
configuration>

注意,在一節中,下的就是我們擴展的行為,providerName則是 MyBehaviorExtensionElement增加的 屬性。如果擴展了IEndpointBehavior,則配置節的名稱為。節負責添加自定義行為的擴展。其 中,中的 name值與下 的對應。

特別注意的是下的 type值,必須是類型的full name。第一個逗點前的內容為完整的類型名(包括命 名空間),第二部分為完整的命名空間。Version,Culture以及 PublicKeyToken也是缺一不可的。每個 逗點後必須保留一個空格,否則無法正確添加擴展行為的配置。這與反射有關,但太容易讓人忽略這一小 細節。希望微軟能在後來的版本中修訂這個瑕疵。

3、在行為擴展的適當方法中,需要添加參數檢查、消息檢查或操作調用程序的擴展。這之間存在一定 的對應關系。對於參數檢查,我們需要在IOperationBehavior接口類型中的ApplyClientBehavior()以及 ApplyDispatchBehavior()中添加。例如對於之前的CalculatorParameterInspector,我們可以定義一個 類CalculatorParameterValidation:

public class CalculatorParameterValidation:Attribute, IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{
}

public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{
CalculatorParameterInspector inspector = new CalculatorParameterInspector();
clientOperation.ParameterInspectors.Add(inspector);
}

public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
CalculatorParameterInspector inspector = new CalculatorParameterInspector();
dispatchOperation.ParameterInspectors.Add(inspector);
}

public void Validate(OperationDescription operationDescription)
{
}
#endregion
}

如果檢查器與擴展行為在職責上沒有分離的必要,一個更好的方法是定義一個類同時實現 IParameterInspector和IOperationBehavior接口,例如:

public class CalculatorParameterValidation:Attribute, IParameterInspector,  IOperationBehavior
{
#region IParameterInspector Members
public void BeforeCall(string operationName, object[] inputs)
{
int x = inputs[0] as int;
int y = inputs[1] as int;
if (x <0 || y < 0)
{
throw new FaultException("The number can not be less than zero.");
}
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue,  object correlationState)
{
//empty;
}
#endregion

#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{
}

public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{
CalculatorParameterInspector inspector = new CalculatorParameterInspector();
clientOperation.ParameterInspectors.Add(thisinspector);
}

public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
CalculatorParameterInspector inspector = new CalculatorParameterInspector();
dispatchOperation.ParameterInspectors.Add(thisinspector);
}

public void Validate(OperationDescription operationDescription)
{
}
#endregion
}

操作調用程序雖然通過IOperationBehavior進行關聯,但確是通過DispatchOperation的Invoker屬性 。假定我們已經定義了一個實現IOperationInvoker的類MyOperationInvoker,則關聯的方法為:

public class MyOperationInvokerBehavior : Attribute, IOperationBehavior
{
#region IOperationBehavior Members
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MyOperationInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
#endregion
}

至於對Dispatch的消息檢查,則可以通過IServiceBehavior,IEndpointBehavior或 IContractBehavior中DispatchRuntime的MessageInspectors添加;而對Client的消息檢查則可以通過 IEndpointBehavior或IContractBehavior中ClientRuntime的MessageInspectors添加(注意,此時與 IServiceBehavior無關,因為它不會作用於客戶端代理)。例如:

 public class PrintMessageInspectorBehavior :  IDispatchMessageInspector,IEndpointBehavior
{
//略去IDispatchMessageInspector接口成員的實現;
 

#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection  bindingParameters)
{
//empty;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher  endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
public void Validate(ServiceEndpoint endpoint)
{
//empty;
}
#endregion
}

如果實現的是IServiceBehavior接口,則需要遍歷ApplyDispatchBehavior()方法中的 ServiceHostBase對象: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase  serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher .Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
}
}

注:IDispatchMessageInspector 接口的方法為BeforeSendReply()和AfterReceiveRequest();而 IClientMessageInspector接口 的方法則為BeforeSendRequest()和AfterReceiveReply()。

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