什么是WCF WCF的全稱是:Windows Communication Foundation。從本質上來說,它是一套軟件開發包,是微軟公司推出的符合SOA思想的技術框架。
WCF為程序員提供了豐富的功能,其中包括:托管、服務實例管理、異步、安全、事務管理、離線隊列等。並且WCF對產業中的標准協議進行了封裝和定義,它把程序員從繁瑣的通信、格式編碼中解放出來,使得程序員能夠專注於業務邏輯的實現。同時,WCF統一了微軟公司之前推出的多種分布式技術,其中包括:
1. Web服務和WSE。
2. .Net Remoting。
3. .Net 企業服務。
4. 微軟消息隊列(MSMQ)。
WCF對這些技術的集成包括兩個方面:
1. WCF的架構本身吸取了這些技術的精華。
2. WCF開發的服務/客戶端可以和現有的Web服務、MSMQ程序進行交互。
2.2 .Net Framework的四大體系架構
.Net Framework的四大體系包括:WCF、WF、WPF、WCS。
WCF(Windows Communication Foundation):主要是用來做松耦合的分布式通訊的,它還有另外一個名字叫Indigo,是微軟邁向SOA一個重要的標志。WCF是.Net Framework的一個子集。
WF(Windows Wordflow Foundation):工作流引擎。
WPF(Windows Presenttation Foundation):WPF使用矢量繪圖引擎,提供了一種聲明式編程語言XAML,用來開發具有炫目視覺效果的應用程序。
WCS(Windows Card Space):采用了一種新的數字標識技術來實現類似網銀單點登錄的功能,主要用來防止釣魚式攻擊等,從網絡安全方面提供了一個保障。
2.3 WCF體系框架
從圖上來看,WCF的體系基本包含了4個方面,分別為契約、服務運行時、消息和寄宿。
1 契約(能干什么)
從SOA的概念上來看,契約屬於一個服務公開接口的一部分。一個服務的契約,定義了服務端公開的服務方法、使用的傳輸協議、可訪問的地址、傳輸的消息格式等內容。
基本上,契約的定義描述了該服務的功能和作用,它告訴SOA系統中的其它節點這個服務是“干什么”的。
2 服務運行時(怎么干)
服務運行時定義了服務在運行時的具體行為。如果說契約描述了服務是“干什么”的,那么服務運行時就在一定程度上描述了服務是“怎么干”的。
3 消息
消息方面包含了消息的傳輸方式、消息的編碼與解碼。消息方面的內容基本屬於服務邊界以內的具體實現。具體的傳遞時限,必須符合在契約中定義的綁定協議。
4 激活和宿主(在哪干)
激活和宿主屬於WCF程序的部署方式。一個WCF服務需要在一個可運行的程序中寄宿,我們可以把宿主理解為WCF運行的容器。常用的寄宿方式包括自寄宿、IIS寄宿、Windows激活服務、Windows服務、Com+組件等。根據SOA的原則,激活和宿主類型的變化不會影響服務本身的特性和外部對該服務的訪問,而WCF在這一方面也確實做的非常出色。
2.4 WCF基礎概念介紹
WCF框架中包含了大量的基礎概念,本小節將以簡短的篇幅帶領大家瀏覽這些概念,使大家能夠對WCF的基本概念有所了解。
1. 地址(Address)
在WCF框架中,每個服務都具有唯一的地址。在SOA系統中,其它服務和客戶端通過服務的地址來對服務進行訪問。一個服務的地址由一個統一資源標示符(URI)來表示。下面是幾個地址示例:
http://localhost /Service
net.tcp://dc3web1:9023/MyService
net.msmq://localhost/MyMsMqService
實際上地址的形式不止這些,它們的構成形式如下所示:
http://[Hostname]:[Port]/[ServiceAddress]
https://[Hostname]:[Port]/[ServiceAddress]
net.tcp://[Hostname]:[Port]/[ServiceAddress]
net.pipe://[Hostname]:[Port]/[ServiceAddress]
net.msmq://[Hostname]/public(private)/[QueueName]
msmq.formatname://{msmq format name}
2. 綁定(Binging)
綁定定義了服務與外部通信的方式。它由一組稱為綁定元素的元素而構成,這些元素組合在一起形成通信基礎結構。一個綁定可以包含以下內容。
1. 通信所使用的協議,如HTTP、TCP、P2P等。
2. 消息編碼方式,如純文本、二進制編碼、MTOM等。
3. 消息安全保障策略。
4. 通信堆棧的其它任何要素。
3. 契約(Contract)
在2.3節中,筆者已經介紹了契約的基本概念。在WCF中一共包含了4種契約,分別是服務契約、數據契約、錯誤契約和消息契約。
1. 服務契約[ServiceContract]
服務契約將多個相關的操作聯系在一起,組成單個功能單元。
2. 數據契約[DataContract]
數據類型的說明稱為數據契約。服務使用的數據類型必須在元數據中進行描述,以使其它各方面可以與該服務進行交互操作。
3. 錯誤契約[FaultContract]
錯誤類型的說明稱為錯誤契約。
4. 消息契約[MessageContract]
消息契約描述消息的格式。
5. 終節點(EndPoint)
終結點是用來發送或接收消息(或同時執行這兩種操作)的構造。一個終節點由三個要素組成,分別是筆者已經介紹了的:地址、綁定和契約。以SOA的思想來看,一個終節點就相當於服務的公共接口。
6. 元數據
服務的元數據描述服務的特征,外部實體需要了解這些特征以便與該服務進行通信。服務所公開的元數據包括XML架構文檔(用於定義服務的數據協定)和WSDL文檔(用於描述服務的方法)。啟用元數據后,WCF通過檢查服務及其終節點自動生成服務的元數據。
在WCF的行為章節中,筆者將介紹兩種WCF的元數據發布方式。
7. 宿主
服務必須承載於某個進程中。宿主是控制服務的生存期的應用程序。
3 第一個WCF程序
在了解了WCF的基本概念以后,本節將按照程序員學習新技術的習慣,給出一個簡單的服務契約的HelloWord實例跟一個數據契約的實例。
3.1 HelloWord服務契約的定義
using System.ServiceModel ;
[ServiceContract]
public interface IService1
{
[OperationContract]
string HelloWord(string name);
}
public class Service1 : IService1
{
public string HelloWord(string name)
{
return name + "說:HelloWord";
}
}
Demo1服務契約
3.2 數據契約的定義
using System.ServiceModel ;
using System.Runtime.Serialization ;
[DataContract]
public class People
{
[DataMember]
public string name;
[DataMember]
public int age;
public People(string name,int age)
{
this.name = name;
this.age = age;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetInfomation(People people);
}
Demo2數據契約
知識點:通過自寄宿在服務器端模擬客戶端
4 WCF消息交換模式
WCF客戶端與服務器之間是通過消息進行通訊,了解WCF的消息交換模式有助於大家對消息的發送和接受由更直觀的理解。在WCF中,有三種消息交換模式:數據報模式、請求-響應模式、雙工模式。
4.1 數據報模式(One-Way Calls)
數據報模式指的是發送端負責把消息發送給對方並且收到確認消息之后,就完成交互的方式。在這種模式下,發送方唯一能確定的就是消息發送成功,而對於消息是否最終到達服務的終節點、是否被成功處理、發揮的結果如何都一無所知。
數據報模式存在以下特點:
1. 返回類型只能是void
2. 不能包含ref或者out類型的參數
3. 只有客戶端發起請求,服務端並不會對請求進行回復。
通過設置OperationContract的IsOneWay=True可以將消息模式設置為數據報模式,具體方法如下:
[OperationContract(IsOneWay=true)]
void Test();
Demo3數據報模式與請求-響應模式
4.2 請求-響應模式(Request/Reply)
在請求響應模式中,客戶端發送一個消息並且接收一個返回消息來完成一次交互。在該模式中,消息的發起端必然是客戶端,並且從服務端返回只有一條消息。客戶端在發送出消息后會阻止當前線程並且等待服務端返回消息。
請求響應模式是缺省的消息交換模式,類似於HTTP協議中的請求/響應模型,這種消息交換模式是使用最多的一種。請求-響應模式有如下特點:
1. 調用服務方法后需要等待服務的消息返回
2. 在這種模式下,服務端永遠是服務端,客戶端就是客戶端,職責分明。
它是缺省的消息交換模式,設置OperationContract便可以設置為此種消息交換模式,方法如下:
[OperationContract]
void Test();
Demo3數據報模式與請求-響應模式
4.3 雙工模式(Duplex)
在雙工模式中,客戶端和服務端都可以任意地向對方發送消息,而對方也可以以任意的次序來接收消息。在這種模式下,發送端和接收端的概念變得不再適用,取而代之的是通信的兩個端點。
這種模式相對較復雜一些,服務端的契約定義如下所示:
public interface ICallBack
{
[OperationContract(IsOneWay = true )]
void UpdateInterval(int seconds);
}
[ServiceContract(CallbackContract=typeof(ICallBack ))]
public interface IService1
{
[OperationContract]
void Heartbeat();
}
Demo4雙工模式
5 WCF綁定
綁定的本質是一個配置好的通道棧,為了方便程序員專注於業務邏輯,WCF提供了一系列常用的綁定。
5.1 標准綁定
在.NET Framework 3.5中WCF一共提供了12種標准綁定,這些綁定基本能夠覆蓋用戶所有的傳輸要求。
表5.1 12種標准綁定簡要說明
綁定名稱 簡要介紹 所需.Net Framework版本
basicHttpBinding 基於WS-I Basic Profile 1.1的Web服務 3.0
wsHttpBinding 針對改進的Web服務的綁定,包括WS-Security,WS-Transaction等元素 3.0
wsDualHttpBinding 提供雙工通信的HTTP綁定 3.0
webHttpBinding 支持REST/POX服務的綁定,使用XML/JSON序列化 3.0
netTcpBinding 使用TCP傳輸協議在跨主機的局域網內使用,支持可靠性、事務、安全等特性、並且該綁定被特別優化來支持WCF系統。但是,使用該綁定需要確保通信雙方都基於WCF構建,這里並不符合SOA的原則 3.0
netNamedPipeBinding 支持和netTcpBinding大致相同的特性,但由於使用命名管道進行通信,所以通信不能跨越主機 3.0
netMsmqBinding 使用微軟消息隊列(MSMQ)協議來進行異步脫機的消息交互。關於該綁定的交互方式,在本書的后續章節中有詳細的介紹 3.0
netPeerTcpBinding 使用P2P協議在網格中進行消息交互 3.0
msmqIntegrationBinding 該綁定可以用來在WCF消息和MSMQ消息中進行轉換 3.0
wsFederationHttpBinding 該綁定支持使用了聯合安全機制的Web服務 3.0
ws2007HttpBinding 該綁定繼承自wsHttpBinding,其主要涉及目的是為了支持2007年新制定的WS標准 3.5
ws2007FederationHttpBinding 該綁定繼承自wsFederationHttpBinding,和wsHttpBinding一樣,其設計目的是為了支持2007年新制定的WS標准 3.5
5.2 設置綁定的方式
在WCF程序中,有兩種方式來設置綁定,在代碼中設置綁定和在配置文件中設置綁定。一般來說,在配置文件中設置綁定更為常用,這是因為綁定的設置常常需要根據部署環境的改變而改變。
1. 在配置文件中設置綁定
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<servicename="WcfServiceLibrary1.Service1" behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<endpoint address ="HelloWord "
binding="wsHttpBinding "
contract="WcfServiceLibrary1.IService1 ">
</endpoint>
</service>
</services>
</system.serviceModel>
</configuration>
2. 在代碼中設置綁定
using System.ServiceModel;
using System.ServiceModel.Channels;
NetNamedPipeBinding binding = new NetNamedPipeBinding ();
EndpointAddress address = new EndpointAddress(new Uri("net.pipe://localhost/HelloWorld "));
using (HelloWorldProxy proxy = new HelloWorldProxy())
{
Console.WriteLine(proxy.HelloWorld("Kevin")); //利用代理調用服務
Console.Read();
}
6 WCF契約
6.1 契約的定義和分類
6.1.1 什么是契約
在2.3節中,筆者已經介紹了契約的基本概念。
契約(Contract)是 WCF 的消息標准,告知客戶端如何與服務器聯系交互。一般情況下,我們用接口(Interface)來定義服務契約(Service Contract)。
6.1.2 契約的分類
在WCF中一共包含了4種契約,分別是服務契約、數據契約、錯誤契約和消息契約。
6.2 服務契約
在第3節中,筆者已經針對服務契約和數據契約做過兩個Demo,因此服務契約和數據契約本節不做重點。
下面來看下服務契約都有哪些屬性:
ServiceContract
? ConfigurationName:其設置信息在配置文件中的名稱。
? Name / Namespace:自定義該服務契約的名稱和命名空間。
? SessionMode:設置服務契約的 Session 方式,包括Allowe、NotAllowed、Required。
? CallbackContract:設置 duplex 模式時的回調類型。
? ProtectionLevel:指定消息保護方式,可以對消息進行加密和簽名處理。
OperationContract
? AsyncPattern:用於定義異步服務方法。
? IsInitiating:指示服務方法能否啟動一個 Session。
? IsTerminating:指示服務方法調用完成是否結束 Session。
6.3 數據契約
在第3節中,筆者已經針對服務契約和數據契約做過兩個Demo,因此服務契約和數據契約本節不做重點。
下面來看下數據契約都有哪些屬性:
DataContract
? Name / Namespace:自定義名稱和命名空間。
DataMember
? Name:自定義名稱。
? IsRequired:指示該成員序列化前必須被賦值。
6.4 錯誤契約
在WCF中消息首先會被序列化為SOAP消息再進行發送。也就是說,所有的數據都包含在SOAP消息中,包括異常信息。SOAP規定了異常消息的攜帶方式,那就是全部放入Fault節點中。Fault節點必須是Body節點的子節點,同時,一個SOAP消息中只能出現一個Fault節點。
表6.1 Fault的子節點
子節點 描述
<Code> 供識別故障的代碼,可包含兩個子節點:Code和Subcode
<Reason> 可供人閱讀的有關故障的說明
<Node> 可選,指向SOAP消息中引發錯誤的節點
<Role> 可選,發生錯誤時節點所處的角色
<Detail> 可選,包含了詳細的錯誤信息
說明:無論是使用哪種錯誤方式:WCF系統最終會把錯誤信息放入Fault節點中進行傳輸。
6.4.1 WCF中默認的異常處理
WCF 將服務異常(Exception)轉換成 SOAP faults,傳遞到客戶端后再次轉換成 Exception。只不過缺省情況下,我們很難從中獲取有意義的信息。
[ServiceContract]
public interface IService1
{
[OperationContract]
int Add(int a, int b);
}
public class Service1 : IService1
{
public int Add(int a, int b)
{
throw new Exception("錯誤");
}
}
異常信息如下所示:
Unhandled Exception: System.ServiceModel.FaultException: The server was unable t
o process the request due to an internal error. For more information about the
error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehavio
rAttribute or from the <serviceDebug> configuration behavior) on the server in o
rder to send the exception information back to the client, or turn on tracing as
per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server t
race logs.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message
reply, MessageFault fault, String action, MessageVersion version, FaultConverte
r faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime operation, ProxyRpc&rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpantim
eout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessageretMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&msgDa
ta, Int32 type)
at ConsoleClient.ServiceReference1.IService1.TestException()
at ConsoleClient.ServiceReference1.Service1Client.TestException() in e:\wcf講
稿(kevin)\demo-kevin\demo5wcf中默認異常處理\consoleclient\service references\s
ervicereference1\reference.cs:line 50
at ConsoleClient.Program.Main(String[] args) in E:\WCF講稿(Kevin)\Demo-Kevi
n\Demo5WCF中默認異常處理\ConsoleClient\Program.cs:line 13
請按任意鍵繼續. . .
6.4.2 WCF中的FaultException
我們將代碼改為如下:
public void TestException()
{
throw new Exception(new FaultException("異常測試").ToString());
}
這次輸出信息要有好的多,異常信息如下所示:
Unhandled Exception: System.ServiceModel.FaultException: 異常測試
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime operation, ProxyRpc&rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpantim
eout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessageretMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&msgDa
ta, Int32 type)
at Client.ServiceReference1.IService1.Add(Int32 a, Int32 b)
at Client.ServiceReference1.Service1Client.Add(Int32 a, Int32 b) in e:\wcf講
稿(kevin)\demo異常處理\異常處理\client\service references\servicereference1\re
ference.cs:line 50
at Client.Program.Main(String[] args) in E:\WCF講稿(Kevin)\Demo異常處理\異
常處理\Client\Program.cs:line 13
請按任意鍵繼續. . .
6.4.3 WCF中的FaultContractAttribute
另外,我們可以通過FaultContractAttribute傳遞更詳細的異常信息給客戶端。
代碼如下所示:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(FaultMessage))]
int Add(inta,int b);
}
[DataContract]
public class FaultMessage
{
[DataMember]
public string Message;
[DataMember]
public interrorCode;
}
public class Service1 : IService1
{
public int Add(int a, int b)
{
FaultMessagefaultMessage = new FaultMessage();
faultMessage.Message = "錯誤信息";
faultMessage.errorCode = 1234;
throw new FaultException<FaultMessage>(faultMessage, faultMessage.Message);
}
}
客戶端的異常信息
Unhandled Exception: System.ServiceModel.FaultException`1[Client.ServiceReferenc
e1.FaultMessage]: 錯誤信息 (Fault Detail is equal to Client.ServiceReference1.Fa
ultMessage).
注意:由於單程操作沒有任何返回值,也不回把任何錯誤發還給客戶端,所以不能在單程操作上使用錯誤契約,不然會導致運行時異常。
6.5 消息契約
使用數據契約已經足以應付消息交互雙方對數據內容的所有要求,但是對消息的格式卻無法全面控制。如果想要定制SOAP消息的Head和Body內容,這就需要定制SOAP消息的Head和Body內容,這就需要用到消息契約。消息契約在實際WCF編程中使用的並不多,本節不做詳細討論,只給出一個簡單的示例:
[MessageContract ]
public interface MyMessage
{
[MessageHeader ]
public intSessionId;
[MessageHeader]
public string Description;
[MessageBodyMember ]
public string A;
[MessageBodyMember]
public string B;
[MessageBodyMember]
public string C;
}
說明:消息中包含Header跟Body兩部分。Header用來存放上下文信息,Body用來存放數據信息。
7 WCF中的行為
行為(Behaviors)指的是那些影響WCF系統運行的設置。行為在宿主和客戶端啟動后發揮作用,WCF系統中行為包括服務行為(Service Behavior)和操作行為(Operation Behavior)。服務行為作用在終節點上,常見的服務行為包括實例控制、並發控制、元數據發布等。操作行為作用於某一服務操作上,常見的操作行為包括事務流設置等。
7.1 實例控制
當服務端接收到客戶端的調用之后,需要把該消息發送到具體的服務實例上。實例管理,就是和所有實例的分配、管理有關的技術統稱。實例管理屬於服務行為的一種,本節將簡要介紹下實例管理。
7.1.1 實例管理的設置
所有的服務行為都通過ServiceBehavior特性來進行設置,實例管理業不例外。實例管理通過ServiceBehavior特性的InstanceContextMode屬性進行設置,該屬性的類型為InstanceContextMode枚舉類型。枚舉類型定義如下:
public enumInstanceContextMode
{
PerSession = 0 ,
PerCall = 1 ,
Single = 2
}
下面將通過一個例子來演示如何為服務類型配置實例策略
using System.ServiceModel;
[ServiceContract]
public interface IService1
{
[OperationContract]
string HelloWord(string name);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession )]
public class Service1 : IService1
{
public string HelloWord(string name)
{
return name + "說:HelloWord";
}
}
7.1.2 PerSession實例策略
PerSession是默認的實例策略,PerSession代表了會話的實例管理模式。會話管理模式相對較為負責,僅僅設置服務行為是不夠的,需要滿足一下3個方面才能在客戶端和服務端之間建立會話。這3個方面是:
1. 服務契約的會話設置
2. 服務行為的實例模式設置
3. 綁定的選擇
7.1.2.1 服務契約的會話設置
服務契約的會話設置通過ServerContract的SessionMode屬性進行設置,SessionMode屬性是枚舉類型,SessionMode定義如下:
public enumSessionMode
{
Allowed = 0,
Required = 1,
NotAllowed = 2
}
服務契約的SessionMode設置如下所示:
[ServiceContract(SessionMode=SessionMode.Allowed)]
public interface IService1
{
[OperationContract]
string HelloWord(string name);
}
7.1.2.2 服務行為的實例模式設置
這里沒有太多可討論的話題,為了使用會話模式,實例模式必須設置為PerSession。
7.1.2.3 綁定的選擇
綁定的選擇需要注意的就是綁定是否支持會話,如basicHttpBinding則無法應用於會話實例管理模式。
7.1.3 PerCall實例策略
當服務端采用了PerCall實例策略后,每個客戶端的請求消息都會被分發到一個新的服務實例上。而一旦這個調用返回后,服務實例則會被銷毀。
7.1.4 Single實例策略
Single實例策略非常類似設計模式中的單件模式,所有的客戶端代理發送的消息都會被應用到同一個服務實例上。
7.2 並發控制
並發的概念也可以理解為多線程,在WCF中,並發也屬於一種服務行為。
7.2.1 並發管理的設置
和實例管理一樣,並發管理業通過服務行為的屬性來設置。並發管理的屬性名為ConcurrencyMode,它的類型是枚舉類型,該定義類型如下:
public enumConcurrencyMode
{
Single = 0,
Reentrant = 1,
Multiple = 2
}
7.2.2 Single並發模式
ConcurrentMode.Single並發模式是默認的並發設置,當設置了Single模式以后,WCF會為服務實例的操作提供同步鎖。Single並發模式的策略較為極端,等同於在整個操作上加上了同步鎖,在很多情況下,這並不是必須的。
7.2.3 Multiple並發模式
ConcurrentMode.Multiple表示WCF不會主動為服務操作添加任何鎖,每個操作都允許客戶端多個請求同時訪問,這樣做的好處是提高了系統的運行效率,防止消息被阻塞。
7.2.4 Reentrant並發模式
本質上,ConcurrentMode.Reentrant與ConcurrentMode.Single模式一樣:在同一時間只允許一個線程訪問服務操作,在訪問進入操作之前必須獲得同步鎖。所不同的是,ConcurrentMode.Reentrant模式解決了ConcurrentMode.Single模式的死鎖問題。
實際上ConcurrentMode.Reentrant模式的設計正是為了解決死鎖問題。當服務操作在調用其它服務,或者回調客戶端操作時,會釋放服務端的同步鎖,這樣就能夠保證在回調消息返回或者調用鏈回到服務端時不會發生死鎖。在一些消息量較大,服務操作中較長時間訪問其它操作的情況下,ConcurrentMode.Reentrant模式能夠有效的保證配對消息能夠在服務操作向外調用時進入操作。
7.3 元數據發布
在以前的小節中,筆者已經介紹了關於元數據的概念。
在WCF中,為了使客戶端獲得這些元數據並且知道如何訪問服務,宿主程序往往會采用某種方式來發布服務的元數據,本節將討論具體元數據的發布技術。
7.3.1 HTTP-GET方式發布元數據
下面的配置文件展示了如何配置元數據行為來允許客戶端使用HTTP-GET方式獲取元數據
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfServiceLibrary1.Service1" behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfServiceLibrary1.Service1Behavior">
<serviceMetadatahttpGetEnabled="True "/>
<serviceDebugincludeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
當啟動了HTTP-GET的元數據獲取方式后,可以嘗試通過瀏覽器來查看服務端的元數據。在地址欄輸入http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/?wsdl
就可以得到下圖的結果:
說明:除了在配置文件中配置元數據行為,還可以通過程序設置是否啟用HTTP-GET,但是筆者不推薦大家這樣去做。在此只是想讓大家知道有這一做法。
7.3.2 MEX終節點方式發布元數據
MEX終節點方式發布元數據配置起來也很簡單,下面請看一個配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfServiceLibrary1.Service1" behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/" />
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding" contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
<endpoint address="mex" binding="mexNamedPipeBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfServiceLibrary1.Service1Behavior">
<serviceMetadatahttpGetEnabled="True"/>
<serviceDebugincludeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
當配置好MEX終節點以后,就可以再VS2008中的客戶端,點擊Add Service Reference…,然后輸入元數據的地址,來獲取服務器端的元數據。本例中的通過mexHttpBinding訪問的元數據地址為http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex,tcp跟namepipe的訪問地址需要根據實際情況來進行構造。具體構造方法請參照以前章節的綁定。