程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
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、行為的作用范圍,可以用如下表格表示:

作用域

接口

影響范圍

服務

終結點

契約

操作

服務

IServiceBehavior

Y

Y

Y

Y

終結點

IEndpointBehavior

 

Y

Y

Y

契約

IContractBehavior

   

Y

Y

操作

IOperationBehavior

     

Y

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;

        }

    }

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

<configuration>

  <system.serviceModel>    

    <services>

      <service name="MessageInspectorDemo.Calculator">

        <endpoint 

behaviorConfiguration="messageInspectorBehavior"

                  

address="http://localhost:801/Calculator"               

    

                  binding="basicHttpBinding" 

                  

contract="MessageInspectorDemo.ICalculator"/>

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="messageInspectorBehavior">

          <myBehaviorExtensionElement 

providerName="Test"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <extensions>

      <behaviorExtensions>

        <add name="myBehaviorExtensionElement"

             

type="MessageInspectorDemo.MyBehaviorExtensionElement, 

MessageInspectorDemo, Version=1.0.0.0, Culture=neutral, 

PublicKeyToken=null"/>

      </behaviorExtensions>

    </extensions>

  </system.serviceModel>

</configuration>

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

特別注意的是<extensions>下的 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