WCF编程权威指南(txt+pdf+epub+mobi电子书下载)


发布时间:2020-05-20 10:44:28

点击下载

作者:周家安

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

WCF编程权威指南

WCF编程权威指南试读:

前言

WCF(Windows Communication Foundation,Windows通信基础框架)是Windows平台上集大成的通信技术,它整合了早期.NET版本中的Remoting(远程技术)、Socket通信,以及基于HTTP协议的Web Service等多种通信技术。WCF与Windows Presentation Foundation(WPF)及Windows Workflow Foundation(WF)并列为新一代.NET的核心类库。

WCF很多时候仅仅被看作是Web Service,这其实是一种误解,或者说是片面的理解。尽管WCF包含了Web Service相关的内容,并且以SOAP消息作为数据传输载体,不过,WCF能够轻松完成普通Web Service不容易完成的任务,例如复杂数据对象的传递、服务回调。除了支持基于HTTP方式通信外,WCF也可以使用TCP、UDP等协议进行通信。既可以寄宿在IIS服务中运行,也可以在独立的应用程序进程(如控制台应用程序)中运行。因此,不能片面地认为WCF是Web Service,应该将其理解为一种综合的通信技术。

正是由于WCF整合了多种通信技术,所以必然会涉及许多复杂的概念与规范。往往会让许多编程入门者望而却步。为了帮助初学者朋友学习WCF,本书的重点不再讲述复杂的概念,转而通过简单的原理讲解与实例演示相结合的方式进行讲述,使初学者朋友们也可以快速地动手实践。笔者建议读者朋友不妨先抛开各种烦琐的概念与规范,直接从编写代码与功能实现入手,当学会如何使用某个知识点后,再通过网络搜索去理解相关的概念。这样做能够减少学习过程中的枯燥感,一定程度上能提高学习兴趣。

本书内容基本覆盖了WCF的方方面面,可大致归纳如下:(1)WCF应用程序的基本结构;(2)认识通信通道与SOAP消息;(3)协定与终结点;(4)配置文件的使用;(5)会话模式与双工通信;(6)路由与服务发现;(7)通信错误的处理;(8)WCF的安全性;(9)扩展WCF的功能;(10)与Web技术集成。

本书主要由周家安编著。此外,邓林、史雅琪也参与了本书部分内容的编写工作。

本书内容适合有.NET编程基础的读者,对于没有任何编程基础的读者朋友,建议先阅读一下.NET编程相关的入门资料,然后再阅读本书。由于作者水平有限,本书难免会有不足,读者朋友在阅读过程中发现有不当之处,可以通过以下方式与作者联系。

电子邮件:csdev2012@foxmail.com

博客:http://www.cnblogs.com/tcjiaan

微博:http://weibo.com/tcjiaan

配书资源链接地址:http://pan.baidu.com/s/lgf5ocZD

最后,必须感谢广大朋友(尤其是提议我撰写本书的网友们)长期以来对我的支持,也感谢盛东亮编辑对本书的写作给予的大力支持。作者2018年1月第1章WCF应用程序基础

作为开篇,本章的主要任务是了解WCF应用程序的基本实现过程。读者可以通过对本章的学习来了解以下内容:● 编写WCF应用项目的基本思路;● ServiceHost类的用法;● 客户端通过服务引用来调用WCF服务;● 使用ChannelFactory类来调用WCF服务。1.1 关于本书示例项目的说明

WCF是Windows平台下各种通信方案的集成技术,它不仅仅可以寄宿在IIS中作为Web服务公开,还可以在独立的Windows进程中运行。也可以将WCF应用封装为类库,以供其他应用程序调用。

本书的所有示例都是基于普通的.NET应用程序项目,可以直接运行,而无须依赖Web服务器组件。因此,读者可以很方便地使用本书示例。1.2 WCF服务的基本实现步骤

本节主要介绍WCF服务器端的实现,客户端的实现将在下一节中讲述。

一般来说,要让WCF服务运行起来,开发者需要依次完成以下几个部分:● 定义协定。主要是服务协定(ServiceContract),从类型角度看,

服务协定是一个接口,接口本身作为服务协定的容器。在服务协

定内部,可以包含N个(N为1个或多个)服务操作协定(OperationContract),映射到接口类型上,服务操作协定是一个

方法。服务操作必须是方法,不能是属性和事件等成员。有些时

候,除了定义服务协定外,还需要定义数据协定(DataContract)

和消息协定(MessageContract),有关这些内容,本书会在后

续的章节中介绍。● 实现服务类。服务协定只是一个接口,它可以在服务器和客户端

之间共享,作为双方用来通信的一种“约定”。服务协定只是一

个接口,并不包含实际的代码实现,因此,需要一个类来实现协

定接口,提供具体的功能,这就是服务类。客户端会通过访问服

务协定接口的某个方法来调用WCF服务,当消息发送到服务器

后,服务器会找到实现协定接口的类,并将其实例化,然后调用

与操作协定匹配的方法(即服务协定接口的实现方法)。例如,

服务协定接口定义了一个Work方法作为服务操作协定,当客户

端访问协定接口的Work方法时,消息会传到服务器,服务器实

例化服务类,并找到类中的Work方法,然后调用,最后把调用

结果发回给客户端。● 寄宿WCF服务。对于Web项目,可以不考虑这一步,因为项目

模板会生成相关的代码,使WCF服务能够在IIS服务上运行。对

于独立进程中的WCF服务,必须使用ServiceHost类来启动服

务,之后客户端才能调用服务。

接下来,请读者参考以下步骤,完成一个简单的WCF服务应用程序。(1)以管理员身份运行VisualStudio,然后新建一个控制台应用程序。由于在进程中寄宿WCF服务时,某些情况下要求运行用户具有管理员权限,因此,建议以管理员身份运行开发环境,这样在调试的时候,管理员权限会传递到示例进程中,以避免因权限不够而运行失败。(2)与WCF相关的许多类型都位于程序集System.ServiceModel中,因此需要在项目中添加对该程序集的引用。(3)定义服务协定接口,本示例中接口的名字为IDemo,代码如下:

服务协定的定义与一般接口类型无异,但是,必须注意的是,要使接口能够作为服务协定被公开,在接口的定义上必须附加ServiceContractAttribute,否则WCF服务在运行时不会将接口识别为服务协定。在服务协定接口内,需要用OperationContractAttribute来标记操作协定,在本例中,Add方法会被视为操作协定公开,但Run方法不会被作为操作协定公开,即客户端是无法调用Run方法的,因为该方法上没有附加OperationContractAttribute。(4)定义一个类,实现服务协定接口,代码如下:

Add方法会向客户端公开,本例将返回两个参数相加的和,而Run方法在前面的协定接口中并没有标注为操作协定,即该方法不会向客户端公开,此处无须添加任何具体代码。

读者需要注意的是,服务器与客户端是通过协定来交互的,即实现服务协定的类不需要对外公开,它只在服务器内部使用,因此该类不应该声明为public,可以定义为internal。由于class默认的访问级别为internal,故本例中可以省略internal关键字。(5)创建ServiceHost实例,寄宿WCF服务。

在实例化ServiceHost类时,需要传递一个表示服务实现类的Type作为参数,注意此处的Type的类型是实现了服务协定接口的类,在本例中为DemoService类,不能使用服务协定接口的Type,因为接口不能实例化。另外,还有一个可选参数——baseAddresses,它是一个URI数组,可以包含多个URI,它表示服务公开的基础地址。

调用ServiceHost类的构造函数后,可以对服务进行一些设置,比如添加服务终结点(EndPoint)。服务终结点用于向客户端公开WCF服务,它需要指定一个有效的地址,本例中指定了一个相对地址demo,结合传递给ServiceHost类构造函数的基址,该服务终结点的地址变为:http://localhost:500/demo。localhost表示本地计算机,端口号为500。如果在调用ServiceHost构造函数时没有指定任何基址,那么在添加终结点时就必须使用完整的URI。比如

服务终结点除了包含URI外,还包含一个绑定(Binding)和一个协定(Contract),协定就是前面所定义的服务协定,而绑定是负责通信层控制的,比如采用HTTP协议还是TCP协议通信、传输数据的缓冲区有多大等。将地址、绑定和协定三者结合起来,组成了终结点的基本功能,即WCF服务将在地址http://localhost:500/demo上公开IDemo服务协定,并以HTTP方式通信,这样一来,客户端就能通过这个地址来找到服务并进行调用了。

设置完ServiceHost的各个参数后,就可以调用Open方法打开服务,只有在服务处于打开的状态下,客户端才能访问。在调用Open方法之前必须完成所有服务配置,如上面代码中,向服务添加了一个终结点,或者可以配置服务器证书等,服务一旦打开,就不能再进行配置了。

配置WCF服务既可以使用代码来完成,也可以使用配置文件,两种方法任选其一即可。使用配置文件的优点在于方便后期修改,比如服务终结点地址的变更,只需要修改配置文件即可,不必重新编译应用程序。本书在后续章节中会讲述如何通过配置文件来配置WCF应用程序。

至此,一个简单的WCF服务应用程序就完成,完整的示例代码请参考\第1章\Example_1。1.3 调用WCF服务

完成上一节的示例后,相信读者已对实现WCF服务的基本思路有所了解。WCF服务器运行后,就可以由客户端来调用。总体来说,客户端可以通过两种方法来调用WCF服务。本节将分别介绍这两种方法。1.3.1 服务引用

这是最简单的方法,在VisualStudio开发环境中,只需要添加服务引用,开发工具就会自动生成客户端代理类,不需要开发者添加任何与服务通信的实现代码,就可以像调用普通类型一样调用服务。

但是,WCF毕竟不同于普通的Web服务,要让WCF服务支持在客户端以添加服务引用的方式生成代理类型,WCF服务必须向客户端公开元数据(Metadata)。元数据将以WSDL文档的形式公开,它的作用是描述服务的一些基本信息,如在哪个地址可以找到服务,服务包含哪些操作可以被调用,调用服务时所使用的输入输出消息的结构……

VisualStudio开发环境是根据WCF服务所公开的元数据来生成客户端代理类的,如果WCF服务不公开元数据,则无法生成客户端代理类。

下面示例将演示一个简单的数学计算服务,调用服务时会传入一个数值,最终服务会计算该数值的二次方,然后将计算结果返回给客户端。如果在调用服务时传入数值5,就会返回数值25。

按照上一节中实现WCF服务的思路,首先应该定义服务协定,代码如下:接着是实现服务协定接口,实现接口的类只在服务器上执行,不需要对外公开。

完成以上工作后,需要实例化一个ServiceHost对象,用以运行WCF服务。

在本示例中,实例化ServiceHost对象后,并没有调用AddServiceEndpoint方法来添加服务终结点,只是在ServiceHost类的构造函数调用时传递了服务类的Type及服务基址。这是因为ServiceHost具有一个默认终结点处理方案,如果代码没有显式调用AddServiceEndpoint方法,那么ServiceHost就会根据基址和服务类自动生成服务终结点。在本例中,基址是http://localhost:500,使用的通信协议为HTTP,所以自动生成的服务终结点所使用的默认绑定为BasicHttpBinding,如果使用的是net.tcp协议,就会使用NetTcpBinding;并且MyService服务类只实现了一个服务协定——IService,所以默认的服务终结点的服务协定将指向IService;由于是默认生成的服务终结点,因此其地址与基址相同。

另外,最为关键的一点是:一定要将一个ServiceMetadataBehavior实例添加到服务的Behavior集合中。行为(Behavior)用来描述服务将支持哪些扩展功能(除调用服务以外的功能),比如是否支持获取元数据、是否把服务器上的错误信息传回给客户端、服务类实例在何时被清理等。在本例中则需要ServiceMetadataBehavior类来使服务支持向客户端公开元数据。在实例化ServiceMetadataBehavior对象后,应将它的HttpGetEnabled属性设置为true,这样客户端才能通过HTTP方式请求WCF服务的元数据。

服务器已经完成,接下来将实现客户端。示例客户端是一个WPF项目,主窗口中包含一个TextBox控件,用来输入一个数值,然后,用户可以单击Button控件来调用WCF服务并获得计算结果,最后,计算结果会显示在TextBlock控件中。相关的XAML代码如下:

此时请运行示例服务器,并确保WCF服务已正确运行。然后打开“解决方案资源管理器”窗口,右击“引用”节点,从弹出的快捷菜单中选择“添加服务引用”,在打开的“添加服务引用”对话框中,输入示例WCF服务的基址,即http://localhost:500,接着单击“转到”按钮。分析完成后,对话框中会显示已找到的服务,如图1-1所示。

为将要生成的代理类型输入一个自定义的命名空间,该名字可根据实际情况自行设置,在本例中可以随意输入,最后单击“确定”按钮。

VisualStudio开发环境会生成三个类型:● 根据服务协定生成一个接口,该接口将与服务协定接口的结构完

全相同。

读者可能会发现,好像跟前面定义的IService服务协定不太一样。其实,这个由开发工具生成的接口和前面WCF服务示例中的服务协定接口是对等的。SqrAsync方法和Sqr方法功能一样,只不过SqrAsync方法支持异步等待而已。也就是说,开发工具生成了两个版本的服务操作方法——Sqr是同步调用的方法,而SqrAsync方法则可以异步等待(能够使用await关键字调用)。图1-1 添加服务引用● 从刚才生成的IService接口与IClientChannel接口派生一个新的接

口。

这只是可以让服务代理类的调用代码能够访问客户端通道(ClientChannel)的一些方法,如可以调用Close方法关闭通信通道。开发者不需要实现IClientChannel接口,因为WCF运行时内部有默认的实现类型。● 最后,还有一个类,它派生自ClientBase,泛型参数

TChannel可以用服务协定接口类型来替换,如本例中,可以用

IService来替换。

基类ClientBase有一个Channel属性,派生类可以通过这个属性来访问服务协定的成员。

生成代理类型后,服务调用就很简单了。以下代码响应Button控件的Click事件,调用WCF示例服务。

上面代码调用的是代理类的异步等待版本,在异步等待期间,禁用Button控件,防止重复向服务器发出请求。

WCF服务的调用结果如图1-2所示。

完整的示例代码请参考\第1章\Example_2。图1-2 服务调用结果1.3.2 通道工厂

通过添加服务引用来生成客户端代理类,可以很简单地调用WCF服务,但是这种方法要求WCF服务必须公开元数据才能使用,如果WCF服务未公开元数据,就无法通过服务引用的方式调用。这种情况可以考虑使用通道工厂。

通道工厂,即ChannelFactory类,不过它是一个抽象类,不能直接使用,因此应当使用它的派生类——ChannelFactory 。泛型参数TChannel指的是服务协定的接口类型。通道工厂公开CreateChannel方法,调用该方法后会返回TChannel类型参数所指定的服务协定的接口类型,使用CreateChannel方法返回的协定实例,就可以调用WCF服务了。尽管服务协定是接口类型,不能实例化,但运行库会自动生成一个代理实例,并以协定接口的类型返回,当代码通过协定接口调用WCF服务时,实际上在内部会由这个代理实例向服务器发送消息。

通道工厂有两种使用方法:第一种是直接调用静态版本的CreateChannel方法,得到服务协定的引用,然后调用WCF服务;第二种是先调用ChannelFactory类的构造函数进行实例化,然后调用实例版本的CreateChannel方法,以获取服务协定的引用,进而调用WCF服务。

下面通过一个实例来演示通道工厂的用法。

使用ChannelFactory类调用WCF服务之前,需要确定以下两点:● 必须知道服务终结点的URI,即在哪里可以找到服务。本示例中,

服务的终结点地址为http://localhost:900/service。● Binding对象,绑定对象负责处理服务器和客户端之间通信的传

输协议相关的参数,参与通信的双方必须使用相同的协议和参

数。因此,服务器与客户端必须使用相同类型的Binding。在本

示例中,将使用BasicHttpBinding。

下面代码将使用通道工厂来调用一个可以完成加法运算的WCF服务。其中,客户端重新声明了服务协定接口,接口代码如下:

由于在服务器上服务协定接口的名字为IDemo,而客户端中重新定义的服务协定接口的名字为ITestService,如果服务器与客户端上的服务协定接口名字不同,双方是无法进行通信的,因为调用消息发送到服务器上后会找不到要调用的服务。但是,若是服务器和客户端都为服务协定设置了相同的Namespace和Name属性,虽然接口的名字不同,但由于在ServiceContractAttribute上指定了相同的属性值,这种情况下,服务器和客户端依然可以正确识别服务协定。

读者可能注意到,上面代码中有这么一行:

服务协定接口可以强制转换为IClientChannel接口,然后调用Close方法来关闭通道。因为运行库内部存在相关的实现类,所以此处的转换是可行的。除了显式转换为IClientChannel接口类型的方法外,客户端在重新定义服务协定时,还可以直接继承IClientChannel接口,比如这样:这样定义之后,当访问IClientChannel接口的成员时就不需要进行类型转换了。

完整的示例代码请参考\第1章\Example_3。第2章消息与通道

在许多开发场景中,开发者只需要专注于对服务协定层的功能上,不过,WCF毕竟是Windows平台上的一种综合通信技术,对SOAP消息和通道层有一定了解后,开发者可以在需要的情况下,对WCF通道进行扩展。虽然并不要求初学者朋友具备扩展通道层的能力,但通过本章的学习,可以帮助初学者理解WCF的通信过程。

本章内容包括以下两点:● SOAP消息的基本结构;● 直接使用通道来完成通信。2.1 消息基础

在服务协定层上,WCF服务的调用如同调用一般的面向对象类型那样方便,但WCF毕竟是一种需要服务器与客户端双方进行通信的技术。当客户端向服务器发出请求时,首先会访问服务协定接口中的某个方法(操作协定,见上一章的内容),将所需的输入参数传递给操作方法;然后运行时会把这些输入参数进行序列化(通常是XML序列化),并创建一条SOAP(SimpleObjectAccessProtocol)消息,再把输入参数序列化后的内容写入SOAP消息的正文(Body)中;随后,通道层会使用特定的通信协议(如HTTP)把消息发送给服务器。

服务器同样是通过通道层接收到客户端发来的SOAP消息,然后读出消息的正文内容;接着将正文内容进行反序列化,就可以得到客户端所传送的输入参数列表;再把这些参数传递给实现了服务协定接口的服务类,这样就可以调用服务方法了。

不管服务方法是否返回void类型的值,都会向客户端返回一条SOAP消息(单向通信除外,本书在后续内容中会介绍)。首先将服务方法的返回值序列化,然后创建一条SOAP消息,再把序列化的返回值写入消息正文,最后经过通道层发送回客户端。

图2-1简单描述了客户端与服务器的通信过程。

SOAP消息其实是一个XML文档,以Envelope为根元素,包含以下两个部分。● 正文(Body,或者叫主体):正文是消息的主要内容,Body元素

下面可以用任何XML内容来充当消息正文。图2-1 WCF通信示意图● 消息头(Header):消息头通常会放置一些附加信息,这些消息

可以是正文的补充,也可以是与正文无关的内容。与正文相同,

消息头可以是任意XML内容。

下面的XML文档表示一条简单的SOAP消息:

其中,正文部分为:

消息头为:2.1.1 创建消息实例

要创建一条SOAP消息,需要用到Message类(位于System.ServiceModel.Channels命名空间)。Message类封装一条SOAP消息,不过要注意,Message类不能直接实例化,因为它是一个抽象类,而且.NET类库也没有公开它的实现类型。要创建Message实例,应该调用它的CreateMessage方法。这是一组重载的静态方法,调用后直接返回一个Message实例引用。

创建消息实例最常用的是以下重载版本的CreateMessage方法:

version指定所使用的SOAP的版本,该参数的值可以从MessageVersion类的静态属性中获取,比如,使用MessageVersion.Soap12属性的值表示的是版本为1.2的SOAP文档规范。action参数为一个非null的字符串,该参数是必需的,它表示的是SOAP消息头中的Action标头,这个标头是一个标准元素,当服务器接收到客户端发来的消息后,会从消息头中检索Action标头的值,通过这个值,服务调度程序才能找到客户端要调用的服务方法。

在定义服务协定时,通过OperationContractAttribute的Action属性来指定一个有效的字符串值,用来唯一标识一个服务操作方法。举个例子,假设有这样一个服务协定:ServiceContract特性指定了服务协定的命名空间为sv-test,而Compute方法的Action值为compute,将它与服务协定的命名空间合起来就是sv-test/compute。如果客户端想要调用Compute服务操作,那么,它所发送的SOAP消息的Header中应该包含一个Action元素,并且与操作协定上所指定的值相同,比如:

也就是说,WCF服务调度程序是通过Action标头来确认客户端到底要调用哪个服务操作的。

下列示例代码将创建一条SOAP消息,并以字符串作为消息正文。

下例将演示以一个自定义类型作为消息的正文,自定义类型的代码如下:

然后把该类型进行实例化:

创建一个新的SOAP消息,并把上面的Employee实例作为新消息的正文:

上面两条消息所输出的XML文本如图2-2所示。

完整的示例源代码请参考\第2章\Example 1。2.1.2 使用消息头

前面章节曾提到过,一条SOAP消息由两部分组成,消息的主要信息会被放进Body元素中,另一部分就是位于Header元素下的消息头。无论是消息正文还是消息头,都是用XML来描述的,因此,消息头的创建与消息正文一样,可以放置任何支持序列化(主要是XML序列化)的类型。

调用CreateMessage方法创建新消息时,并不会创建自定义的消息头。要向消息实例添加消息头,应该访问Message类的Headers属性,它会返回一个对MessageHeaders类的引用,然后再调用MessageHeaders对象的Add方法来添加消息头。对于标准的消息头(如Action、To、MessageId等)可以直接通过MessageHeaders类的

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载