WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]

元数据的导出就是实现从ServiceEndpoint对象向MetadataSet对象转换的过程,在WCF元数据框架体系中,元数据的导出工作由MetadataExporter实现。MetadataExporter是一个抽象类型,定义了导出元数据的基本行为。WCF定义一个具体的MetadataExporterWsdlExporter,将基于某个终结点的元数据导出生成基于WSDL的MetadataSet。我们先来认识MetadataExporterMetadataSet

一、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 SchemaWS-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之间的箭头表示匹配关系。

image

图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个元素之外,还包含通过WS2007HttpBindingNetTcpBinding导出的一些WS-Policy策略断言。

image

2 通过IE查看导出的元数据

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2009-12-23 19:46 Artech 阅读(...) 评论(...) 编辑 收藏