元數據的導出就是實現從ServiceEndpoint對象向MetadataSet對象轉換的過程,在WCF元數據框架體系中,元數據的導出工作由MetadataExporter實現。MetadataExporter是一個抽象類型,定義了導出元數據的基本行為。WCF定義一個具體的MetadataExporter:WsdlExporter,將基於某個終結點的元數據導出生成基於WSDL的MetadataSet。我們先來認識MetadataExporter和MetadataSet。
一、MetadataExporter
MetadataExporter是一個定義在System.ServiceModel.Description命名空間下抽象類型,下面的代碼片斷給出了MetadataExporter的定義。MetadataExporter定義了3個與元數據導出相關的方法,其中ExportContract僅僅導出基於某個服務契約相關的元數據,ExportEndpoint則導出某個終結點相關的所有元數據。這兩個方法並不直接返回用於承載元數據信息的MetadataSet對象,而是將導出的元數據暫存於元數據轉換的上下文中,最終通過GetGeneratedMetadata方法從該元數據轉換上下文中將導出的元數據提取出來。
1: public abstract class MetadataExporter
2: {
3: public abstract void ExportContract(ContractDescription contract);
4: public abstract void ExportEndpoint(ServiceEndpoint endpoint);
5: public abstract MetadataSet GetGeneratedMetadata();
6:
7: public Collection<MetadataConversionError> Errors { get; }
8: public PolicyVersion PolicyVersion { get; set; }
9: public Dictionary<object, object> State { get; }
10: }
此外, MetadataExporter還定義了三個屬性Errors、PolicyVersion和State。Errors是一個MetadataConversionError對象的集合,包含一些在進行元數據導出過程中出現的錯誤或者警告消息,我們可以利用它來進行一些相應的異常處理;字典類型的State可以作為一個容器盛放一些在進行元數據導出過程中動態使用到的對象;而PolicyVersion代表元數據基於的WS-Policy規范的版本。PolicyVersion的定義如下,由於定義的構造函數是私有的,所以不能直接利用new操作符創建該對象,只能通過定義在PolicyVersion中的兩個靜態只讀屬性Policy12和Policy15得到代表WS-Policy 1.2和WS-Policy 1.5的PolicyVersion對象。靜態屬性Default代表默認的WS-Policy版本,目前為WS-Policy 1.2。屬性Namespace表示相應WS-Policy版本的命名空間。
1: public sealed class PolicyVersion
2: {
3: //其他成員
4: private PolicyVersion(string policyNamespace);
5: public static PolicyVersion Default { get; }
6: public string Namespace { get; }
7: public static PolicyVersion Policy12 { get; }
8: public static PolicyVersion Policy15 { get; }
9: }
WCF定義了一個具體的MetadataExporter類型用於將終結點導出為基於WSDL的MetadataSet,即WsdlExporter。
二、WsdlExporter
通過《元數據(Metadata)架構體系全景展現[WS標准篇]》的介紹,我們知道了元數據具有3三種主要的表現形式:XML Schema、WS-Policy策略和WSDL,而且WSDL可以直接采用XML Schema表示Web服務使用到的數據和消息類型,采用基於WS-Policy的策略斷言定義其綁定行為,基本上一個WSDL文檔可以用於表示Web服務的所有信息。
正是因為WSDL是目前描述Web服務做好的語言,建立WCF終結點與WSDL元素之間的匹配關系,以及基於該匹配關系的元數據導入和導出的實現,是WCF元數據框架體系的一個最為重要的目標。在第1節對WSDL的介紹中,我們已經談過了WCF下終結點三要素(地址、綁定和契約)與組成一份完成WSDL文檔(基於WSDL 1.1)的5個元素之間的匹配關系,現在我們進行一個簡單的總結。組成WSDL的5個元素(Service、Binding、PortType、Message和Type)與終結點三要素之間的匹配關系大體上可以通過圖1來體現,其中WSDL元素之間的箭頭代表引用關系,WSDL和ServicePoint之間的箭頭表示匹配關系。
圖1 WSDL各元素和終結點三要素之間的匹配關系
從圖1我們不難看出:WSDL中Service元素的一個Port元素實際上就代表著整個ServiceEndpoint對象,Port下的Address元素即終結點的地址;WSDL中的Binding元素實際上和終結點的綁定表示相同的內容;而終結點的契約則和一個PortType元素相匹配。
既然WSDL和ServiceEndpoint之間存在著一個如此清晰的匹配關系,那麼直接將一個ServiceEndpoint對象導出成一個基於WSDL的MetadataSet就不會是一件很難的事情,WsdlExporter就是為實現這樣的目標而設計。
在具體對WsdlExporter進行介紹之前,我們不妨先來看看WsdlExporter的定義。從下面給出的代碼片斷中,我們可以看到WsdlExporter直接繼承MetadataExporter。除了重寫定義在MetadataExporter三個抽象方法之外,還定義了一個ExportEndpoints方法幫助我們將一個包含多個終結點的服務作為一個整體導出,因為一個WSDL本身就是對一個完整的Web服務的描述。
1: public class WsdlExporter : MetadataExporter
2: {
3: //其他成員
4: public override void ExportContract(ContractDescription contract);
5: public override void ExportEndpoint(ServiceEndpoint endpoint);
6: public void ExportEndpoints(IEnumerable<ServiceEndpoint> endpoints, XmlQualifiedName wsdlServiceQName);
7: public override MetadataSet GetGeneratedMetadata();
8:
9: public ServiceDescriptionCollection GeneratedWsdlDocuments { get; }
10: public XmlSchemaSet GeneratedXmlSchemas { get; }
11: }
此外,WsdlExporter還定義了兩個只讀屬性,GeneratedWsdlDocuments屬性以ServiceDescription集合的形式返回導出生成的WSDL文檔;GeneratedXmlSchemas則返回導出生成作為描述數據和消息類型的XML Schema。
三、 實例演示:如何通過WsdlExporter導出元數據
為了讓讀者更見深刻地認識WsdlExporter,我們現在做一個簡單的實例演示。我們通過一個簡單的控制台(Console)應用作為演示程序。首先我們先演示如何利用WsdlExporter導出一個終結點,為此我們定義了一個處理訂單的服務契約,契約接口和使用到的數據類型(數據契約)定義如下:
1: using System;
2: using System.ServiceModel;
3: namespace Artech.MetadataExporting
4: {
5: [ServiceContract(Namespace="http://www.artech.com/")]
6: public interface IOrderService
7: {
8: [OperationContract]
9: void ProcessOrder(Order order);
10: }
11: }
1: using System.Collections.ObjectModel;
2: using System.Runtime.Serialization;
3: namespace Artech.MetadataExporting
4: {
5: [DataContract(Namespace="http://www/artech.com/types/")]
6: public class Order
7: {
8: [DataMember]
9: public string OrderId { get; set; }
10: [DataMember]
11: public string CustomerId { get; set; }
12: [DataMember]
13: public Collection<OrderDetail> Details { get; set; }
14: }
15:
16: [DataContract(Namespace = "http://www/artech.com/types/")]
17: public class OrderDetail
18: {
19: [DataMember]
20: public string OrderId { get; set; }
21: [DataMember]
22: public string ProductId { get; set; }
23: [DataMember]
24: public int Quantity { get; set; }
25: }
26: }
接下來,通過下面的代碼創建兩個ServiceEndpoint對象和一個表示服務有效名稱(QName)的XmlQualifiedName對象,傳入WsdlExporter的ExportEndpoints方法。通過調用GetGeneratedMetadata方法獲取包含有所有導出元數據的MetadataSet對象,並將其寫入到一個XML文件中。最終調用Process的靜態Start方法打開該XML文件。
1: using System.Diagnostics;
2: using System.ServiceModel;
3: using System.ServiceModel.Description;
4: using System.Text;
5: using System.Xml;
6: namespace Artech.MetadataExporting
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: ContractDescription contract = ContractDescription.GetContract(typeof(IOrderService));
13: ServiceEndpoint endpoint1 = new ServiceEndpoint(contract, new WS2007HttpBinding(),
14: new EndpointAddress("http://127.0.0.1/orderservice"));
15: ServiceEndpoint endpoint2 = new ServiceEndpoint(contract, new NetTcpBinding(),
16: new EndpointAddress("net.tcp://127.0.0.1/orderservice"));
17: XmlQualifiedName serviceName = new XmlQualifiedName("OrderService", "http://www.artech.com/services/");
18: WsdlExporter exporter = new WsdlExporter();
19: exporter.ExportEndpoints(new ServiceEndpoint[] { endpoint1, endpoint2 }, serviceName);
20: MetadataSet metadata = exporter.GetGeneratedMetadata();
21: using (XmlWriter writer = new XmlTextWriter("metadata.xml", Encoding.UTF8))
22: {
23: metadata.WriteTo(writer);
24: }
25: Process.Start("metadata.xml");
26: }
27: }
28: }
由於本機采用IE作為開啟XML文件默認的應用程序,當上面的代碼成功執行後,包含有元數據的XML文件會通過IE打開。圖2是運行後的截圖,從圖中我們可以看出導出的元數據由6個MetadataSection構成。所有MetadataSection的元數據方言(Dialect)集中在WSDL和XML Schema兩種,其中基於XML Schema方言的MetadataSection描述了我通過數據契約定義的Order和OrderDetail類型的XML結構;基於Order類型的輸入消息和輸出消息的XML結構;以及所有CLR基元類型(Primary Type,比如int、double和DateTime等)。而所有基於WSDL方言的MetadataSection共同構建了一份反映服務的WSDL文檔。該WSDL除了包含WSDL基本的5個元素之外,還包含通過WS2007HttpBinding和NetTcpBinding導出的一些WS-Policy策略斷言。
圖2 通過IE查看導出的元數據