WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]
元数据的导出就是实现从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查看导出的元数据
- 腾讯AI让二子,柯洁还是输了
- AI创业者的“英雄联盟”,腾讯AI加速器二期项目招募开启
- PyTorch发布一周年:盘点社区和工程大事件,后来者居上态势已显?
- 2018年AI如何发展?普华永道做出了8点预测 | 报告下载
- 不正之风!机器学习论文里都有哪四大投机取巧的写作手法?
- 前端写一个月的原生 Android 是怎样一种体验?
- 给人挖矿还不自知 电脑已变黑客肉鸡
- 反序列化漏洞屡被黑客利用,危害巨大,代码怎样写才安全?
- Mifa 主题微信编辑器
- Mifa GitHub Pages 主题
- Mifa Design:一个服务于 Markdown 的设计体系
- 未来机器人大脑将获取互联网知识自我学习
- 【架构拾集】: Android 移动应用架构设计
- Dore 混合应用框架 —— 基于 React Native 的混合应用迁移方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 自动删除QQ空间指定好友的留言
- 在Ubuntu 18.04中安装VMware工具
- 微信小程序下拉刷新功能
- 详解Linux Screen让程序保持后台运行
- Python Des加密与解密实现软件注册码、机器码
- Excel VBA 在保留原单元格数据的情况下,将计算的百分比加在后面
- 入门级别的面试题——LeetCode题目19:删除链表的倒数第N个节点
- python做web接口测试零散笔记--1
- 要一遍做对——LeetCode题目20:有效的括号
- 双指针算法练习(一)
- 一般是面试的热身题——LeetCode题目21:合并两个有序链表
- LeetCode题目22:括号生成
- OpenGL ES 3.0 | 着色器编译器
- LeetCode题目23:合并K个排序链表
- LeetCode题目24:两两交换链表中的节点