WCF编程权威指南/微软技术开发者丛书

WCF编程权威指南/微软技术开发者丛书
作者: 周家安
出版社: 清华大学
原售价: 69.00
折扣价: 49.00
折扣购买: WCF编程权威指南/微软技术开发者丛书
ISBN: 9787302482208

作者简介

内容简介

第3章 协定 第3章协定 WCF通过SOAP消息来传递数据,数据从一端发送到另一端需要经过序列化和反序列化的过程,为了使服务器与客户端都能正确识别SOAP消息中传递的内容,WCF需要一系列的协定。 协定可以为通信的双方构建一种“约定”,服务器和客户端可以重新定义用于通信的类型,但无论怎么定义,都必须遵守协定中的声明。WCF开发中用到的协定有服务协定、*作协定、数据协定、消息协定。本章将逐一讲述以上各种协定。 3.1服务协定与*作协定 前面在了解WCF服务的基本开发步骤时,读者已经接触过服务协定和*作协定了。从类型角度看,服务协定是一个接口,而*作协定就是接口中的一个方法。因此,服务协定包含若干个*作协定(至少需要一个方法声明为*作协定)。 下面代码演示一个简单的服务协定的声明: [ServiceContract] pu**ic interface IWorker { [OperationContract] void Run(dou**e k); [OperationContract] int CheckProgress(); } 在接口上应用ServiceContractAttribute表示它将作为服务协定公开,接口中要作为*作协定的方法必须应用OperationContractAttribute,如果方法没有应用OperationContractAttribute,那么它不会作为服务*作被公开。 再看一个例子: [ServiceContract] pu**ic interface IActions { [OperationContract] int Sum(int[] srcs); float **g(float a, float b, float c); } 在上述代码中,只有Sum方法才会作为服务*作,而**g方法是不会被服务公开的,因为它没有应用OperationContractAttribute。 3.1.1服务协定的命名空间与名称 在声明服务协定时,可以**命名空间(Name Space)和协定名称(Name),这两个属性值都会应用于服务所公开的元数据(WSDL文档)中。Namespace属性将作为WSDL文档中portType元素的命名空间,如果不**该属性的值,将使用默认值http://tempuri.org。而Name属性则应用于WSDL文档中portType元素的名称。 假设有这样一个服务协定: [ServiceContract] interface IDemo { [OperationContract] int Add(int x, int y); } 在声明服务协定后,可以用下面的代码来获取协定的命名空间和名称。 ContractDescription ctdesc = ContractDescription.GetContract(typeof(IDemo)); string s = $"服务协定名称:{ctdesc.Name}\n"; s += $"服务协定的命名空间:{ctdesc.Namespace}"; Console.WriteLine(s); 调用静态的GetContract方法可以返回一个ContractDescription实例,它包含服务协定相关的信息。执行上述代码后,会输出以下内容: 服务协定名称: IDemo 服务协定的命名空间: http://tempuri.org/ 从以上输出信息可以看到,如果ServiceContractAttribute的Namespace属性与Name属性没有被显式设置,则默认的命名空间为http://tempuri.org/,默认的协定名称与作为服务协定的接口名称相同(示例中为IDemo)。 接下来,再声明一个服务协定: [ServiceContract(Namespace = "http://test.com", Name = "MyTask")] interface ITest { [OperationContract] void StartTask(); } 依然使用ContractDescription类来获取上述服务协定的信息。 ContractDescription cd = ContractDescription.GetContract(typeof(ITest)); Console.WriteLine($"服务协定名称:{cd.Name}\n服务协定命名空间:{cd.Namespace}"); 执行后输出以下结果: 服务协定名称: MyTask 服务协定命名空间: http://test.com 命名空间可以是任何符合XML规范的字符串,由于服务器的URI具有**性,通常服务协定的命名空间都会使用服务器的URI。如果不想使用URI来定义命名空间,也可以使用其他字符串,例如myservice、my_service、WorkerService等,字符串中不要带有空格。 有关本示例的完整代码请参考\第3章\Example_1。 3.1.2*作协定的Action值 由于*作协定是包含在服务协定中的,因此它不需要声明命名空间,不过,有两个属性值是应该注意的。与服务协定相似,OperationContractAttribute类也有一个Name属性,用于声明*作协定的名称,默认是方法的名字。例如, [ServiceContract] interface ITest { [OperationContract] void Run(); } 在上面代码所声明的服务协定中包含一个*作协定,因为Name属性未**,则使用默认值——即方法的名字Run。当然,根据需要,可以为Name属性设置一个字符串值。 *作协定还有一个比较重要的属性Action,客户端在调用服务时,会把要调用的*作方法的Action值加入到SOAP消息的Action消息头中(作为Header元素的子级),当消息发送到服务器时,服务调度程序会根据消息头Action的内容来寻找客户端要调用的服务*作。也就是说,Action属性是用来设置*作协定的标识的,调度程序通过客户端传入消息的Action头与服务中各个*作方法的Action属性值进行匹配,如果找到就调用相关的*作,如果未找到匹配项,就会发生错误。 还可以将Action属性的值设置为*(星号),这表示客户端传入的任意SOAP消息都与该*作匹配,不管传入消息的Action头的内容是什么,此*作都能被调用。 与Action属性对应的,还有一个ReplyAction属性,它表示SOAP消息中答复消息的Action头的内容。通常,客户端调用服务*作后(单向通信除外),不管服务*作是否有数据要返回(包括返回类型为void),都会向客户端发回一条SOAP消息(如果返回值为void,就返回一条body为空的消息),这条从服务器返回的消息的Action头的内容就是ReplyAction属性所**的值。 可以使用以下代码来获取服务协定中所包含的*作协定的信息。 ContractDescription cd = ContractDescription.GetContract(typeof(ITest)); foreach (OperationDescription op in cd.Operations) { Console.WriteLine($"*作名称:{op.Name}"); Console.WriteLine("----------------------------------"); foreach (MessageDescription msg in op.Messages) { Console.WriteLine($"Action = {msg.Action}"); } } OperationDescription类用于描述每个*作协定相关的信息。此处要注意,要获取*作协定的Action值,应当先访问其Messages集合。因为本示例的*作协定默认是双向通信,所以它会包含两条消息(接收的客户端请求消息和服务器回应给客户端的消息)。 上面代码运行后的输出结果如下: *作名称:Run ---------------------------------- Action = http://tempuri.org/ITest/Run Action = http://tempuri.org/ITest/RunResponse 服务协定使用的是默认的命名空间,从上面输出可以看到,Action值是由服务协定命名空间与*作名称组成。对应于OperationContractAttribute类,…/Run就是Action属性的值,而…/ RunResponse就是ReplyAction属性的值。 有关本示例的完整代码请参考\第3章\Example_2。 3.1.3直接把服务类声明为服务协定 在WCF服务开发过程中,考虑到服务类不会向客户端公开,一般是先将一个接口类型声明为服务协定,再单独用一个类来实现服务协定。比如这样: [ServiceContract] interface ITaskManager { [OperationContract] void StartRun(); } class TaskManagerSvr : ITaskManager { pu**ic void StartRun() { // … } } 但是,读者不妨观察一下ServiceContractAttribute类的声明,如下: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, …)] pu**ic sealed class ServiceContractAttribute : Attribute { … } 从应用的AttributeTargets值可以得知,ServiceContractAttribute类不仅可以应用于接口,还可以应用于类。这说明,类也是可以声明为服务协定的,当然指的是服务器上的代码,因为客户端不需要具体的实现代码,实现代码是在服务端执行的,客户端只是发送基本的调用信息就够了。 下面请看一个示例,参考源代码位于\第3章\Example_3。 首先,对于服务器,可以直接将服务类声明为服务协定。代码如下: 微软*有价值专家原创作品!微软(中国)有限公司开发体验和平台合作事业部**阅读!微软**执行副总裁沈向洋作序!附赠完整源代码!!本书主题如下: (1) WCF应用程序的基本结构 (2) 通信通道与SOAP消息基础 (3) 协定与终结点 (4) 用配置文件来配置WCF (5) 会话模式与双工通信 (6) 服务路由技术 (7) 服务发现 (8) 通信错误的处理 (9) 安全与授权 (10) 扩展WCF的功能 (11) Web集成与RSS服务 附赠程序代码:提供了全书第1章~**1章的完整实例代码。