第二章 寄宿WCF服务
【摘要】
本章先描述WCF service的工作原理;然后阐述寄宿WCF服务的各种方式;随后通过创建WPF程序和Windows Service来寄宿WCF服务;最后讲述支持WCF内置的各种绑定以及如何对一个WCF服务实现多重绑定。
在第一章中,我们为你介绍了如何创建一个 WCF服务,并将其部署到IIS上,然后通过客户端程序访问该服务。在本章中,我们将关注WCF工作的细节;各种寄宿WCF服务的方式;此外,你还将学习 到如何创建和配置宿主程序,其负责处理来自客户端的请求并控制WCF服务的状态;你还将进一步了解到绑定的工作原理及WCF运行时如何使用绑定来实现服务 的其他特性。
【正文】
1 WCF服务的工作原理
从功能方面讲,WCF服务不外乎是一个对外公 布了可供客户端调用的一系列操作的对象。当创建一个服务时,通过使用服务契约来描述该服务的操作;并创建一个类来实现该服务契约。为了运行服务,必须为服 务对象提供一个运行时环境,该环境使服务能被客户端程序访问。寄宿服务的宿主程序提供运行时环境。从第一章,我们可以得知IIS能够提供这样的运行时环 境。当然,你可以自行创建其他种类宿主程序,只要该程序能执行下列任务:
- 启动和停止WCF服务
- 侦听客户端的请求并且引导这些请求到服务
- 服务端向客户端返回响应
为了理解宿主程序是如何工作的,我们先来了解服务端点及WCF运行时如何使用服务端点的绑定元素。下图展示了一个端点典型构成:端点=地址(A)+绑定(B)+契约(C)。
(1)端点。一个应用程序对外提供一个或者多个端点,以供外部应用程序访问。WCF服务的端点包含三个元素
| 地址(Address) | 服务的地址受多个因素影响,包括它所使用的传输协议;因为不同的传输协议使用不同形式的地址空间。 |
| 绑定(Binding) | 绑定用以描述客户端如何连接到服务端及服务端所需的数据的格式。绑定包含下列信息:
|
| 契约 (Contract) | WCF服务契约是存储在.NET Framework组件中的一个接口,并且用ServcieContract特性标识。服务契约描述了该服务所有的操作,这些操作通过 OperationContract特性标识。任何操作的输入和输出数据都必须进行序列化。服务契约还规定了数据契约,数据契约用来描述复杂数据及如何序 列化这些复杂数据。服务对外公布服务契约的描述信息,以使客户端程序能准确地调用相应的操作以及使用正确的消息格式想服务端发送请求消息。 |
(2)处理客户端请求
一个服务能同时响应多个客户端的请求。为了达 到这个目的,宿主程序需处理多个客户端的请求,并将服务端的响应返回到对应的客户端。此外,宿主程序必须确保服务端和客户端之间的消息应满足其绑定所定义 的安全性、可靠性和事务性方面的需求。幸运的是,你不必自己亲自写这些代码,因为WCF运行时环境为客户端和服务端提供一系列的通道对象用以实现上述需 求。
通道根据服务端配置文件中的绑定信息,执行消 息处理流程中的对应任务;比如,传输通道通过指定的传输协议进行通信;事务通道确保会话的一致性。WCF为每个传输协议都内建了相应的通道。WCF还为每 种类型的通道提供了不同的数据编码,安全管理,可靠性和事务性支持。WCF运行时将这些通道组合成一个通道堆栈。所有服务端和客户端的消息都进入该堆栈中 对应的通道。该堆栈中每一个通道以某种方式传输消息,当完成一个消息传送并将结果输出后,下一个消息进入该通道。通道堆栈双向运行:从接客户端接受到的消 息经过该栈后传送至服务端;从服务端的返回消息经过该堆栈后通过网络回传给客户端。如果通道堆栈不能处理消息,它将产生一个错误,并将给错误消息返回至客 户端,客户端的消息将不再继续处理。
当你启动服务时,WCF运行时读取服务配置文 件中的端点信息并为配置文件中的每个地址创建一个侦听器。当请求达到该侦听器后,WCF运行时根据配置文件中的某个具体地址对于的绑定信息创建一个通道堆 栈,然后引导来自客户端的数据通过该堆栈。如果一个消息通过堆栈中所有的通道,该请求将被传送给一个实例化的服务对象进行处理。
前面已经提到,WCF服务能同时处理来自多个 客户端应用程序的请求。为了完成这个目标,WCF运行时创建多个同时存在的服务实例。 WCF运行时创建InstanceContext对象用以控制通道堆栈和服务实例之间的互操作。你可以通过InstatnceContext对象来修改服 务实现类的ServcieBehavior特性,以修改WCF运行时创建服务实例的方式。ServieBehavior特性类有一个 InstanceContextMode属性,它具有下列值:
| 值 | 描述 |
| InstanceContextMode.PerCall | 当客户端调用服务的一个操作时,该服务就创建一个新实例。当调用完成,服务实例回收该实例。 |
| InstanceContextMode.PerSession | 如果服务实现了Session;当session启动时创建一个新实例,当Session结束时,回收该实例。一个客户端能在session存活时间内多次调用该服务。但是每个服务实例只能访问一个session. 更多信息,请参考第七章。 |
| InstanceContextMode.Single | 只创建一个服务的实例。该实例能被所有客户端和sessions共享。此实例在第一个客户端访问该服务时创建。 |
默认的InstanceContextMode是PerCall。 你可以按照下列方式修改InstanceContextMode:
准备工作:安装和配置WAS。由于WAS在Windows 7上并没有默认安装和配置。你需要用管理员身份执行下列步骤:
Windows—开始菜单—控制面板—程序
添加删除Windows特性
选择Windows process activation service及其子项目;并选择.net framework 3.5及其子项目。然后点击OK
当安装完毕之后,请检查IIS对应的ASP.NET的版本。如果不是4.0版本,请在Visual Studio Command Prompt窗口中执行命令:aspnet_regiis -iru
(1)配置宿主环境使WCF服务支持TCP协议
用管理员身份运行IIS管理工具
在iis管理工具中,选择"编辑绑定"
站点绑定对话将将出现,如果你成功安装了WAS,那么将显示该网站的默认绑定协议。从下图中可以看到,WAS所采用的net.tcp协议将默认监听808端口。如果你想修改该端口,点击"Edit"按钮。在本章的例子中,我们将使用默认的端口。
关闭站点绑定对话框。
展开站点,选择ProductsService,并选择高级设置,对该服务添加tcp.net协议。
关闭高级设置对话框。
现在,ProductsService的宿主环境被配置为使用HTTP和TCP协议侦听请求。
(2)配置客户端应用程序,使其能通过TCP协议连接WCF服务
按照下面的文件接口复制第一章的ProductsClient项目
由于我们已经在IIS上部署了ProductsService,并且该服务使用HTTP和TCP协议,因为我们需要在ProductsClient项目中更新服务引用的地址
然后在弹出的对话框中输入地址:
然后点击OK,Visual Studio将会自动更新。
更新完毕后,打开app.config文件,你会发现在配置文件中,多了一个endpoint和与之对应的banding信息。
修改Progrom.cs,更新proxy的定义。ProductsServcieClient proxy = new ProductsServcieClient("NetTcpBinding_IProductsServcie");
编译你的项目,然后查看运行结果。你将会发现,与第一章的结果完全一致;区别只不过是采用ProductsClient使用TCP传输协议连接到ProductsService罢了。
3 在应用程序中寄宿WCF服务
除了IIS或WAS,你还可以使用其他的方式寄宿WCF服务:
- 你可以创建一个Windows程序寄宿WCF服务,用户使用它来启动和停止WCF服务
- 你可以在Windows服务中寄宿WCF服务,这样只要Windows运行,你的服务也会一直运行
- 你还可以在WF服务程序中寄宿WCF服务,这其实是Windows程序寄宿WCF服务的扩展,当然这种方式有其独有的定义和实现服务的方式。
在本章的后续内容中,你将会看到如何创建Windows应用程序和Windows服务来寄宿WCF服务。在第八章将学习如何使用WF来寄宿WCF服务。在我们开始练习前,你应该了解ServiceHost类。
(1)使用ServiceHost类
到目前为止,我们所创建的WCF宿主程序都能 自动执行服务。如果你创建一个自己的程序,而不使用IIS或者WAS,你可以通过使用System.ServiceModel命名空间下的 ServiceHost类来完成这样的任务。ServiceHost对象可以从包含服务类的组件中实例化一个服务对象;通过绑定信息来配置服务的端点,绑定信息可以功过配置文件或者代码方式进行设置;设置服务所需的安全需求;为你所指定的每个地址创建侦听对象。
你通过指定服务实现类的类型,创建一个ServiceHost对象。你可以按照下列方式指定ServiceHost侦听请求的地址:
ServiceHost productsServiceHost = new ServiceHost(typeof(ProductsService), new Uri[] { new Uri("http://localhost:8000/ProductsService/ProductsService.svc "), new Uri("tcp.net://localhost:8080/TcpProductsService ") });
上述例子使用了在第一章中创建的 ProductService服务。它使用了两个地址:第一个采用HTTP传输,第二个使用TCP。严格的来讲,你在ServcieHost构造函数中指 定的地址是基本地址。基本地址只是地址初始化的一部分。当你在配置文件中设置了更详细的地址信息,那么这个详细的地址信息将和基本地址合并。比如:
ServieHost(Type serviceTpye, Uri[]baseAddresses)
实例化 ServiceHost
--> InitializeDescription(serviceType, new UriSchemekeyedCollection(baseaddressessed))
Initializes a description of the service hosted based on its type and specified base addresses
根据服务的类型和基本地址,实例化Servicehost
---->base.InitializeDescription(baseAddresses);
Creates and initializes the service host with the contract and service descriptions
创建和实例化ServieHost,并指定服务契约和服务描述
------>CreateDescription(out dictionary);
创建Servicehost的描述
------>ApplyConfiguration();
从配置文件中加载服务描述信息,并应用到在WCF运行时已经创建的ServiceHost对象上。
当你创建了ServcieHost对象,你可以通过使用open方法开始侦听请求:
productsServiceHost.Open();
启动ServiceHost对象,会让 WCF运行时检查服务每个端点的绑定配置,并且在每个端点的地址上开始侦听。启动一个服务需要耗费一定时间。使用一个重载的方法可以通过指定时间间隔来启 动服务,如果在指定的时间内服务未能启动,那么将会抛出异常。此外,ServiceHost还支持用异步方式(BeginOpen和EndOpen)启动 服务。
通过调用ServiceHost的Close方法停止一个服务。Close使WCF运行时停止侦听请求,并且彻底地关闭服务。在服务被完全关闭前,处在运行状态的任务将被执行完。和启动服务一样,你也可以用异步方式(BeginClose和EndClose)关闭服务。
ServiceHost提供了一些事件,以供追踪ServieHost对象的状态。下表列出了这些事件:
| 事件 | 描述 |
| Opening | 当通信对象转换到正在打开状态时发生 |
| Opened | 当通信对象转换到已打开状态时发生 |
| Closing | 当通信对象转换到正在关闭状态时发生 |
| Closed | 当通信对象转换到已关闭状态时发生 |
| Faulted | 在通信对象转换到出错状态时发生 |
| UnknownMessageReceived | 接收未知消息时发生 |
4 使用WPF寄宿WCF服务
创建WCF服务类库
创建一个WCF服务类库的新项目(项目名:ProductServiceLibrary;方案名:ProductServiceLibrary)
删除默认的 IService.cs,Servcie.cs和app.config;复制第一章的IProductsServcie和ProductService到 该项目中,并且将这两个文件包括到项目中。并且删除两个文件中的using System.ServiceModel.Web;
添加第一章ProductsEntityModel项目生产的DLL引用;添加Service.ServiceModel和System.Data.Entity引用。
此时,你的项目应该是这样的:
生成项目,确保没有警告和错误。
创建WPF寄宿WCF
添加一个新的WPF项目(ProductsServiceHost)到解决方案ProductServiceLibrary中.
引用System.ServiceModel和ProductsServiceLibrary项目,重命名MainWindow.xaml为HostController.xaml。
打开App.xaml,修改StartupUri为HostController.xaml
添加两个button控件(一个启动WCF服务,一个停止服务),一个label控件,一个textbox控件(显示WCF服务的状态)到HostController.xaml;其xaml文件与下面类似(不用完全一致,你完全可以自己定义控件的名字,位置等)
绑定
描述
BasicHttpBinding
适用于与符合 WS-Basic Profile 的 Web 服务(例如基于 ASP.NET Web 服务 (ASMX) 的服务)进行的通信。此绑定使用 HTTP/HTTPS 作为传输协议,并使用文本/XML 作为默认的消息编码
BasicHttpContextBinding
BasicHttpBinding的扩展,支持并使用HTTP Cookies来存储和传输上下文消息。
WS2007HttpBinding
一个安全且可互操作的绑定,可为 Security, ReliableSession 的正确版本和 TransactionFlow 绑定元素提供支持。关于细节,将在第七章中介绍。
WSHttpBinding
一个安全且可互操作的绑定,适合于非双工服务约
WSHttpContextBinding
WSHttpBinding的扩展,实现了通过SOAP消息的头部信息来收发上下文消息。
WSDualHttpBinding
一个安全且可互操作的绑定,适用于双工服务协定或通过 SOAP 媒介进行的通信
WebHttpBinding
可用于为通过 HTTP 请求(而不是 SOAP 消息)公开的 WCF Web 服务配置终结点
WS2007FederationHttpBinding
一个安全且可互操作的绑定,它派生自 WS2007HttpBinding 并支持联合安全性。
WSFederationHttpBinding
一个安全且可互操作的绑定,支持 WS 联合协议并使联合中的组织可以高效地对用户进行身份验证和授权。
NetTcpBinding
一个安全且经过优化的绑定,适用于 WCF 应用程序之间跨计算机的通信
NetTcpContextBinding
NetTcpBinding的扩展,实现了通过SOAP消息的头部信息来收发上下文消息。
NetNamePipeBinding
一个安全、可靠且经过优化的绑定,适用于 WCF 应用程序之间计算机上的通信
NetMsmqBinding
一个排队绑定,适用于 WCF 应用程序之间的跨计算机的通信
MsmqIntegrationBinding
适用于 WCF 应用程序和现有消息队列(也称为 MSMQ)应用程序之间跨计算机的通信
下图是内置绑定的类图(WebHttpBinding不包括在内,因为其属于Syswem.ServiceModel.Web命名空)
(2)配置Bindings
本章后续内容将介绍通过用代码实例化绑定,使 用该绑定创建端点;然后使用AddServiceEndpoint方法将该端点添加到ServiceHost类。类似地,在客户端你可以通过代码方式添加 绑定到程序(第十一章将有详细的示例)。但是,通常情况下,都是通过配置文件来设置服务端和客户端的绑定信息。我们以ProductsClient程序的 配置文件为例:
<system.ServiceModel>
...
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="" />
</protocolMapping>
...
</system.ServiceModel>
6 在Windows Services中寄宿WCF服务
在结束本章之前,你将了解到另外一寄宿WCF服务的方式,并且还可以学习到如何使用代码方式为服务添加端点。
在程序中寄宿WCF服务依赖用户启动和停止服 务,这要求当前登录到Windows系统的用户不能注销。一个较好的解决办法是在Windows服务中寄宿WCF服务。通过这种方式,你可以设置在开机时 自动运行一个Windows服务,当然,系统管理员可以在需要的时候停止或重启该服务。
在下面的例子中,你将创建一个Windows服务用以寄宿ProductServcie服务,该服务只允许本机客户端程序访问该服务,并且该服务使用命名管道传输协议侦听固定的地址。
创建Windows服务
创建WindowsProductsService项目和方案
创建好项目后,冲命名Service1.cs为ServiceHostController.cs;在弹出的对话框中,点击"是";以更新项目中有关信息。
添加引用:System.ServiceModel, System.Runtime.Serialization和System.Data.Entity.
添加引用:ProductsEntityModel.dll,IProductsService.cs,ProductsService.cs,以及app.config;注意app.config将仅仅保留数据库连接字符串部分。
添加逻辑部分
注意:
你不可以通过添加服务引用的方式添加使用NetNamePipeBinding绑定创建的服务;
如果你在Windows服务中寄宿WCF时,采用HTTP绑定协议,你还需要做高级设置,详细内容请查询MSDN。
在域环境下,上述测试项目或许有错误。因为根据我的经历,在公司的电脑上执行返回结果为List时的请求是,报错。而家中电脑一切OK。如果有人知道详情,请指点。
7 总结
本章为了展示了如何创建WCF服务的宿主程 序。你已经看到了使用各种不同的程序(WAS,WPF,Windows服务)来寄宿WCF服务。你也已经了解了如何指定WCF绑定的传输协议,编码方式, 及其他非服务功能方面的特性,比如可靠性、安全性和事务性。我们还为你介绍了WCF内置的各种绑定。你还学习了如何为一个WCF指定多个绑定。最后你还了 解到了如何通过配置文件和编写代码的方式设置绑定信息。
【附加内容】
有园子里的朋友,让找WCF整个框架的东西。本人不敢妄自菲薄,只能发一张图给大家。我相信,随着本书翻译过程的进行,大家对每不部分都熟悉了之后,再回头来看WCF的框架,必定有全面而深刻的认识。
-----其他参考-----
http://msdn.microsoft.com/zh-cn/library/ms731079.aspx 基本术语
http://msdn.microsoft.com/en-us/library/ms733107.aspx 端点
http://msdn.microsoft.com/en-us/library/ms731088.aspx channel
http://msdn.microsoft.com/en-us/library/aa347789.aspx 数据传输架构
http://msdn.microsoft.com/en-us/magazine/cc163412.aspx WCF Address in depth
http://msdn.microsoft.com/en-us/magazine/cc163447.aspx WCF Messaging Foundation
http://msdn.microsoft.com/en-us/magazine/cc163394.aspx WCF binding in depth