前言:
- 在前面的文章中,我們定義服務協定時,在它的操作方法上都會加上OperationContract特性,此特性屬於OperationContractAttribute 類,將OperationContract應用於方法,以指示該方法實現作為服務協定(由 ServiceContractAttribute 屬性指定)一部分的服務操作。OperationContractAttribute 屬性聲明方法是服務協定中的操作。 只有具有 OperationContractAttribute 屬性的方法可作為服務操作公開。 不含有 OperationContractAttribute 標記的所有方法的服務協定不公開任何操作。公開的操作方法我們稱之為操作協定。
操作協定的屬性:
- Action: 獲取或設置請求消息的 WS-Addressing 操作。 WCF 根據請求消息的操作將它們調度至方法。
- 使用 Action 屬性控制方法的輸入消息的操作。 由於 WCF 使用該操作將傳入消息調度至相應方法,因此在協定操作中使用的消息必須具有唯一的操作。 默認操作值由以下幾項組成:協定命名空間(默認值為“http://tempuri.org/”)、協定名稱(如果沒有使用顯式服務接口,則為接口名稱或類名)、操作名稱,並且如果該消息是一個相關的響應,則還有一個附加字符串(“Response”)。 您可以使用 Action 屬性重寫該默認值。
- 若要指示服務操作可處理該服務接收的所有消息,但又不能定向到服務操作,請指定值“*”(星號)。 這種類型的操作(稱為不匹配的消息處理程序)必須具有下列方法簽名之一,否則會引發 InvalidOperationException :該服務操作只能接受一個 Message 對象,並且返回一個 Message 對象;該服務操作只能接受一個 Message 對象,並且不返回任何內容(即返回 void)。
- 服務協定只能有一個 Action 屬性設置為“*”的服務操作。 當 IsInitiating 屬性設置為 false 時,服務類實現的相同 listenUri 所承載的任何服務協定組可具有多個 Action 屬性設置為“*”的服務操作。 然而,其中只有一個服務操作可將 Action 屬性設置為“*”,並將 IsInitiating 屬性設置為 true。
- AsyncPattern :屬性指示使用 Begin/End 方法對可以實現或異步調用該操作。
- 使用 AsyncPattern 屬性生成可在服務器和/或客戶端異步調用的服務操作。 AsyncPattern 屬性通知運行庫 Begin 方法有一個符合 .NET Framework 異步方法設計模式的匹配的 End 方法。 生成用以實現服務操作的服務器異步方法可增強服務器的可伸縮性和性能,而不會影響服務的客戶端。如果服務操作在執行完可異步執行的較長操作后,必須將某些內容返回至客戶端,建議使用此方法。
- 這不會影響客戶端,因為服務器上的異步方法對是實現詳細信息,該信息不會影響操作的基礎 Web 服務描述語言 (WSDL) 描述。 此類方法在客戶端顯示為包含 <input> 和相關 <output> 消息的單個操作。 WCF 自動將入站消息路由至 Begin<methodName> method and routes the results of the End<methodName> 調用的結果路由至出站消息。 因此,客戶端信道可將方法對表示為單個同步操作或一個異步操作對。 客戶端表示形式在任何情況下都不會以任何方式影響服務器上的異步實現。
- 客戶端協定可使用 AsyncPattern 屬性指示異步方法對,即客戶端可使用該方法對異步調用操作。 通常,客戶端應用程序使用 ServiceModel 元數據實用工具 (Svcutil.exe) 工具和 /async 選項生成客戶端可以調用使用異步操作的 Begin<methodName> 和 End<methodName> 方法對。
- 如果服務操作具有異步和同步兩個版本,則服務上的默認行為是調用同步版本。
- IsOneWay :獲取或設置一個值,該值指示操作是否返回答復消息。
- 使用 IsOneWay 屬性可以指示操作不返回答復消息。 這種類型的操作對通知或事件樣式通信十分有用,特別是雙向通信。 如果不等待基礎響應消息,則單向操作的調用方在處理請求消息時無法直接檢測錯誤。
- 在面向雙工(或雙向)服務的應用程序中,客戶端和服務器相互獨立地通信,並且客戶端信道可使用其方法中的 IsOneWay 屬性指示服務可單向調用客戶端(該客戶端可作為事件處理)。 這不會返回調用或生成消息,因為該服務不需要任何響應消息。
- 如果 IsOneWay 屬性設置為 false(默認值),即使返回 void 的方法也會生成答復消息。 在此種情況下,基礎結構將創建並發送一條空消息,以向調用方指示該方法已返回內容。(通過此方法使基礎結構可以將 SOAP 錯誤發送回客戶端。)將 IsOneWay 設置為 true 是取消創建和調度響應消息的唯一方法。
- 單向方法不得返回一個值或具有 ref 或 out 參數;否則將引發 System.InvalidOperationException 異常。
- 指定操作是單向操作,只表示它沒有響應消息。 如果無法建立連接、出站消息非常大或該服務無法足夠快地讀取入站信息,則可能會阻止。 如果客戶端要求非阻止調用,則會生成 AsyncPattern 操作。
- IsInitiating: 獲取或設置一個值,該值指示方法是否實現可在服務器上啟動會話(如果存在會話)的操作。
- ServiceContractAttribute.SessionMode 的值必須為 Allowed 或 Required 且使用的綁定必須要求或允許會話,IsInitiating 屬性才能正常工作。
- 默認為 true,這意味着操作可以是通道上調用的第一個操作。 除了調用該方法之外,后續的調用對於啟動方法無效。 不會創建其他任何會話。 如果協定不使用會話,則將 IsInitiating 設置為 false 會被忽略。通常,將 IsInitiating 設置為 false 可強制客戶端在調用此方法之前,調用服務上的另一個方法。
- IsInitiating 和 Action 屬性之間存在交互操作。 服務協定只能有一個 Action 屬性設置為“*”的服務操作。 當 IsInitiating 屬性設置為 false 時,服務類實現的相同偵聽 URI 所承載的任何服務協定組可具有多個 Action 屬性設置為“*”的服務操作。 然而,其中只有一個服務方法可將 Action 屬性設置為“*”,並將 IsInitiating 屬性設置為 true。
- 如果服務收到非啟動操作的消息,則該服務返回 ActionNotSupported SOAP 錯誤。 客戶端將這種情況當作 FaultException。 如果客戶端首先調用非啟動操作,則客戶端運行庫會引發 System.InvalidOperationException。
- IsTerminating: 獲取或設置一個值,該值指示服務操作在發送答復消息(如果存在)后,是否會導致服務器關閉會話。
- 使用 IsTerminating 屬性指示調用服務操作可終止通信會話。
- 在客戶端應用程序中,將 IsTerminating 值設置為 true 以指示 WCF 在答復到達后,關閉信道。
- 在服務中,如果客戶端在該期間內不關閉信道,則將會設置計時器並中止信道。
- 如果調用方偵聽的是 OperationContractAttribute.IsTerminating 操作的 OperationContext.OperationCompleted 事件,則在收到響應時可能會阻塞。 處理這種情況的正確方法是,當引發 OperationCompleted 時在其他線程上調度工作,然后從該事件處理程序立即返回。
- ProtectionLevel 獲取或設置一個值,該值指定是否必須對操作的消息進行加密和/或簽名。
- 運行時的保護行為是在下列屬性中設置的保護級別值的組合,這一點很重要。 這些屬性具有層次結構。 除非已為較窄范圍顯式設置了某個不同的值,否則設置最外層的值將為所有較窄的范圍確定默認設置。 在這種情況下,外層的值將保持所有較窄的范圍的默認設置,但特定的設置除外。例如,如果將 ServiceContractAttribute.ProtectionLevel 設置為 ProtectionLevel.EncryptAndSign,並且其他較窄范圍都沒有設置保護級別,則會對操作協定中的所有消息進行加密和簽名。 但是,如果其中一個操作將 ProtectionLevel 設置為 ProtectionLevel.Sign,那么對此操作的消息只進行簽名,而對協定中的所有其他消息進行加密和簽名。
- 當協定上沒有顯式指定保護級別並且基礎綁定支持安全性時(無論處於傳輸級別還是處於消息級別),整個協定的有效保護級別將為 ProtectionLevel.EncryptAndSign。 如果綁定不支持安全性(如 BasicHttpBinding),則整個協定的有效 System.Net.Security.ProtectionLevel 為 ProtectionLevel.None。 因此,根據終結點綁定,即使協定指定了 ProtectionLevel.None,客戶端也可以要求不同的消息或傳輸級別安全保護。
- 消息保護級別的層級結構:
- ReplyAction:獲取或設置用於該操作答復消息的 SOAP 操作的值。
- 除指定答復消息操作標頭的特定值以外,還可以指定字符串“*”(星號)。 在服務中指定星號可指示 WCF 不向消息中添加答復操作,如果您是直接對消息進行編程會十分有用。 在客戶端應用程序中指定星號可指示 WCF 不驗證答復操作。
WCF操作協定示例:
- 解決方案如下:
- 工程結構說明:
- Service:服務契約定義和實現。在服務契約接口中,我們定義了MethodOne、MethodTwo、MethodThree三個操作契約,其中MethodThree使用異步實現,這個示例也說明了異步服務的實現。在服務契約中,我們啟用會話要求,將SessionMode設置為Required,目的是為了驗證IsInitiating(啟動服務器會話)和IsTerminating(服務操作在發送答復消息后,關閉會話)。
設置了操作契約Action(設置請求消息的 WS-Addressing 操作)和ReplyAction(設置用於該操作答復消息的 SOAP 操作的值)
ISampleMethod.cs的代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace Service { [ServiceContract(Name = "SampleMethodContract", Namespace = "http://wangweimutou.SampleMethodContract", SessionMode=SessionMode.Required)] public interface ISampleMethod { /// <summary> /// IsInitiating默認值為true,IsTerminating默認值為false /// </summary> /// <param name="msg"></param> [OperationContract(Name="OCMethodOne", AsyncPattern=false, IsInitiating=true, IsTerminating=false, Action = "http://wangweimutou.SampleMethodContract/RequestMethodOne", ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodOne")] string MethodOne(string msg); [OperationContract(Name = "OCMethodTwo", AsyncPattern = false, IsInitiating = true, IsTerminating = false, Action = "http://wangweimutou.SampleMethodContract/RequestMethodTwo", ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodTwo")] string MethodTwo(string msg); [OperationContract(Name = "OCMethodThree", AsyncPattern = true, IsInitiating = true, IsTerminating = false, Action = "http://wangweimutou.SampleMethodContract/RequestMethodThree", ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodThree")] IAsyncResult BeginMethodThree(string msg, AsyncCallback callback, object asyncState); string EndMethodThree(IAsyncResult result); } }
SampleMethod.cs的代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.ServiceModel; namespace Service { public class SampleMethod :ISampleMethod { public string MethodOne(string msg) { return "You called MethodOne return message is: " + msg; } public string MethodTwo(string msg) { return "You called MethodTwo return message is: " + msg; } public IAsyncResult BeginMethodThree(string msg, AsyncCallback callback, object asyncState) { return new CompletedAsyncResult<string>(msg); } public string EndMethodThree(IAsyncResult r) { CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>; return "You called MethodThree return message is: " + result.Data; } } class CompletedAsyncResult<T> : IAsyncResult { T data; public CompletedAsyncResult(T data) { this.data = data; } public T Data { get { return data; } } #region IAsyncResult Members public object AsyncState { get { return (object)data; } } public WaitHandle AsyncWaitHandle { get { throw new Exception("The method or operation is not implemented."); } } public bool CompletedSynchronously { get { return true; } } public bool IsCompleted { get { return true; } } #endregion } }
2. Host:控制台應用程序,服務承載程序。添加對Service程序集的引用,實現以下代碼。Program.cs的代碼如下:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using Service; using System.ServiceModel; namespace Host { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(SampleMethod))) { host.Opened += delegate { Console.WriteLine("服務已經啟動,按任意鍵終止!"); }; host.Open(); Console.Read(); } } } }
App.config代碼如下:

<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <service name="Service.SampleMethod" behaviorConfiguration="mexBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:1234/SampleMethod/"/> </baseAddresses> </host> <endpoint address="" binding="wsHttpBinding" contract="Service.ISampleMethod" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="mexBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
3. Client:控制台應用程序。客戶端應用程序,啟動Host服務承載程序,添加對服務地址http://localhost:1234/SampleMethod/引用后,將命名空間設置為ServiceRef,
勾選生成異步操作復選框,生成異步客戶端代理類(參照WCF初探-11:WCF客戶端異步調用服務),我們就可以對程序進行同步和異步的調用了。Program.cs代碼如下:
運行程序,結果顯示如下:
從結果中,我們可以看到,程序停止了對MethodTwo和MethodThree的調用,這是因為MethodOne的IsTerminating設置為true,所以客戶端代理在調用完MethodOne
后就關閉了對服務器會話的支持。接下來,我們將調用MethodOne的代碼注釋掉,編譯Client后再次運行,會得到一下結果:
由於我們將MethodTwo的IsInitiating設置為了false,導致服務器會話沒有啟動,所以服務調用失敗。接下來,我們將MethodOne、MethodTwo、MethodThree三個操作
契約的IsInitiating和IsTerminating分別設置為true和false,也就是設置為默認值,重新編譯程序后,運行客戶端我們可以看到如下結果:
接下來,我們再來查看一下操作契約設置的屬性值,從上面的客戶端程序代碼可以看出,服務契約和操作契約的方法和名稱都改成了設定值,如MethodOne變成了OCMethodOne。
打開客戶端測試程序,添加對服務地址的引用后,我們就可以看到消息的請求和響應,如觀察到MethodOne調用的結果如下:
總結:
- 在上面的示例中,我們修改了操作契約的部分屬性,也從運行結果和交換的消息中驗證了這些修改的屬性。並且還完成了異步服務的實現。關於消息保護級別屬性將在以后的博文中做解析。
- 關於OperationContract的IsOneWay可以查看以下博文:
WCF初探-5:WCF消息交換模式之雙工通訊(Duplex)
- 關於客戶端異步調用服務可以查看以下博文: