設計和實現服務協定
創建服務協定—WCF術語
消息
消息是一個獨立的數據單元,它可能由幾個部分組成,包括消息正文和消息頭。
服務
服務是一個構造,它公開一個或多個終結點,其中每個終結點都公開一個或多個方法。
終結點
終結點是用來發送或接收消息(或執行這兩種操作)的構造。終結點包括一個定義消息可以發送到目的地的位置(地址,一個描述消息如何發送的通信機制規范(綁定)以及對於可以再該位置發送或接收(或者兩者皆可)的一組消息的定義(服務協定)——該定義還描述了可以發送何種消息。
WCF服務作為一個終結點集合向外界公開。
創建服務協定
定義服務協定
在類或接口上使用ServiceContractAttribute屬性標記
定義服務操作
在方法上使用OperationContractAttribute屬性對其進行標記
參數和返回值
每個操作都有一個返回值和一個參數,即使它們為void。可以使用局部方法將對對象的引用從一個對象傳遞到另一個對象,但與局部方法不同的是,服務操作不會傳遞對對象的引用,它們傳遞的是對象的副本。這是因為參數或返回值中使用的每個類型都必須是可序列化的,換言之,該類型的對象必須是能夠轉換為字節流,並能夠從字節流轉換為對象。
默認情況下,基元類型是可序列化的,.NET Framework中的很多類型都是可序列化的。
服務操作的消息模式1——請求/答復
通過請求/答復模式,請求發送方(客戶端應用程序)將接收與請求相關的答復。這是默認的模式,因為它既支持傳入操作(一個或多個參數傳遞到該參數中),也支持返回操作(將一個或多個輸出值傳回給調用方)
[OpertaionContract]
String SayHello(string name);
注意:除非指定其他的基礎消息模式,否則,即使服務操作返回void,也屬於請求/答復消息交換。
操作結果是:除非客戶端異步調用操作,否則客戶端將停止處理,直到收到返回消息,即使該消息正常情況下為空也是如此。
優點
響應消息中可返回SOAP錯誤,這表明可能在通信或處理中發生了一些與服務有關的錯誤狀況。
缺點
如果執行操作需要很長的時間,則會降低客戶端性能和響應能力。
代碼演示
1) 新建一個WCF服務庫
2) 默認生成文件目錄如下:
注:ISayHelloService和SayHelloService為修改過的文件名
3) 在ISayHelloService.cs文件中聲明SayHello函數
[ServiceContract] public interface ISayHelloService { [OperationContract] string SayHello(string name); }
4) 在SayHelloService.cs文件中實現該接口
public class SayHelloService : ISayHelloService { public string SayHello(string name) { return "Hello " + name; }
5) F5運行該WCF項目,會彈出調試框,可進行調試。如下:
請求/答復模式調試效果圖
服務操作的消息模式2——單向
如果WCF服務應用程序的客戶端不必等待操作完成,並且不處理SOAP錯誤,則該操作可以指定單向的消息模式。
單向操作是客戶端調用操作,並在WCF將消息寫入網絡后繼續進行處理的操作。通常意味着,除非在出站消息中發送的數據極其龐大,否則客戶端幾乎立即繼續運行(除非發送出錯)。此種類型的消息交換模式支持從客戶端到服務應用程序的類似事件的行為,即:及時響應。
若要為返回void的操作指定單向消息交換,將OperationContact的IsOneWay屬性設置為true即可,默認為false。如下所示:
[OperationContract(IsOneWay=true)]
String SayHello(string name);
此方法與前面的請求/答復示例相同,但是將IsOneWay屬性設置為true意味着盡管方法相同,服務操作也不會發送返回消息,而客戶端將在出站消息抵達通道層時立即返回。
服務操作的消息模式3——雙工
雙工模式的特點是,無論使用單向消息發送還是請求/答復消息發送方式,服務和客戶端均能夠獨立的向對方發送消息。對於必須直接與客戶端通信或向消息交換的任意一方提供異步體驗(包括類似於事件的行為)的服務來說,這種雙向通信形式非常有用。
由於存在與客戶端通信的附加機制,雙向模式比請求/答復或單向模式要略為復雜。
若要設計雙工協定,還必須設計回調協定,並將該回調協定的類型分配給標記服務的ServiceContractAttribute屬性(attribute)的CallbackContract屬性(property)。
若要實現雙工模式,必須創建第二個接口,該接口包含在客戶端調用方法聲明。
代碼演示
1) 在原有的文件結構基礎上添加如下文件:
ICalculatorService.cs接口文件和CalculatorService.cs類文件
項目目錄文件如下:
2) 接口文件ICalculatorService.cs文件,主要定義了算術操作接口,和一個回調接口,代碼如下:
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples",
SessionMode=SessionMode.Required,
CallbackContract=typeof(ICalculatorCallBackService))]
//添加ServiceContract服務協定標記,並為其屬性賦值,其屬性含義為: NameSpace:Web服務描述語言中<portType>元素的命名空間, SessionMode:設置協定模式為必須要求會話綁定, CallbackContract:設置其回調協定(本例中即獲取的回調接口的類型) interface ICalculatorService//服務端算術操作接口,均為單向模式 { [OperationContract(IsOneWay = true)] void Clear(); [OperationContract(IsOneWay = true)] void AddTo(double n); [OperationContract(IsOneWay = true)] void SubtractFrom(double n); [OperationContract(IsOneWay = true)] void MutiplyBy(double n); [OperationContract(IsOneWay = true)] void DivideBy(double n); } interface ICalculatorCallBackService//客戶端要實現的回調接口 { [OperationContract(IsOneWay = true)] void Equals(double result); [OperationContract(IsOneWay = true)] void Equation(string result); }
3) 實現接口ICalculatorService的接口文件CalculatorService.cs類文件
該類文件主要實現了ICalculatorService接口中定義的類。具體代碼如下:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] //該類添加了ServiceBehavior屬性,用來指定服務協定實現的內部執行行為,並設置其屬性InstanceContextMode為PerSession,即為每一個會話創建一個新的InstancContext對象 public class CalculatorService:ICalculatorService //實現ICalculatorService的所有方法 { double result; string equation; ICalculatorCallBackService callback = null;//定義一個接口對象 public CalculatorService() { result = 0.0D; equation = result.ToString(); callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallBackService>(); //獲取調用當前操作的客戶端實例的通道,實例化上邊定義接口對象 } public void Clear() { callback.Equation(equation + "=" + result.ToString()); result = 0.0D; equation = result.ToString(); } public void AddTo(double n) { result += n; equation +="+"+ n.ToString(); callback.Equals(result); } public void SubtractFrom(double n) { result -= n; equation += "-" +n.ToString(); callback.Equals(result); } public void MutiplyBy(double n) { result *= n; equation += "*" + n.ToString(); callback.Equals(result); } public void DivideBy(double n) { result /= n; equation += "/" + n.ToString(); callback.Equals(result); } }
4) 修改配置文件
因為我們的這個項目 是在上一個項目上添加的,所以需要在配置文件中再進行一些添加。
在<services></services>節點內添加一個新的<service></service>節點,如下:
<service name="WCF.CalculatorService" behaviorConfiguration="WCF.CalculatorBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8732/Design_Time_Addresses/WCF/CalculatorService/"/>//服務地址 </baseAddresses> </host> <endpoint address="" binding="wsDualHttpBinding" contract="WCF.ICalculatorService"/>//算術操作協定 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>//元數據協定 </service>
5) 在解決方案中添加一個控制台應用程序,Program.cs中定義一個CallbackHandler類,該類實現了服務端的回調接口ICalculatorServiceCallback,代碼如下
public class CallbackHandler :ICalculatorServiceCallback { public void Equals(double n) { Console.WriteLine("Result{0}", n); } public void Equation(string equ) { Console.WriteLine("Equation({0})", equ); } }
Program類具體代碼如下:
class Program { static void Main(string[] args) { InstanceContext instanceContext = new InstanceContext(new CallbackHandler());//創建一個服務實例的上下文信息對象,用來為初始化算術服務的客戶端實例做准備 CalculatorServiceClient calculator = new CalculatorServiceClient(instanceContext);//創建一個算術服務的客戶端實例,並在下邊調用服務端的方法進行計算,在計算方法中又調用客戶端的回調方法,顯示操作結果 Console.WriteLine("Hello Olive,Let's begin"); Console.WriteLine(); double value = 100.00D; calculator.AddTo(value); value = 50.00D; calculator.SubtractFrom(value); value = 12.00D; calculator.MutiplyBy(value); value = 3.00D; calculator.DivideBy(value); calculator.Clear(); Console.WriteLine(); Console.ReadLine(); } }
創建數據協定
面向服務的應用程序(例如:WCF程序)設計為與Microsoft平台和非Microsoft平台上的最大可能數量的客戶端應用程序進行互操作。
為了獲得最大可能的互操作性,建議使用DataContractAttribute和DataMemberAttribute屬性對類型進行標記,以創建數據協定。
數據協定是服務協定的一部分,用於描述您的服務操作交換的數據。
如下代碼所示:
[DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } }
數據協定是可選的樣式協定:除非您顯示應用數據協定屬性,否則不會序列化任何類型或數據成員。
數據協定與托管代碼的訪問范圍無關:可以對私有數據成員進行序列化,並將其發送到其他位置,以便可以公開訪問它們。
WCF處理用於啟用操作功能的基礎SOAP消息的定義,並處理數據類型到消息正文的序列化和從消息正文進行的反序列化。數據類型一旦序列化,就無需在設計操作時考慮基礎消息交換基礎結構。
可以使用其他序列化機制。標准ISerializable,SerializableAttribute和IXmlSerializable機制都可以用於處理數據類型到基礎SOAP消息的序列化,這些消息可將數據類型從一個應用程序帶到另一個應用程序。
Out和Ref參數
大部分情況下,可以使用in參數out和ref參數,由於out和ref參數都指示數據是從操作返回的,類似如下的操作簽名會指定需要請求/答復操作,即使操作簽名返回void也是如此。
使用out或ref參數要求操作具有基礎響應消息,才可以將已修改的對象傳回。如果操作是單向操作,則將在運行時引發InvalidOperationException異常。