当需要和服务通信时WCF为客户端提供了丰富的API。通过Service.ServiceModel实现的API处理将.NET类型转换成XML然后从客户端向服务端发送消息。你可以直接用API编程,或者你可以使用工具生成一个代理类和配置文件。在这一部分,我们将首先说明如何使用代码直接调用服务,然后我们将使用工具实现这个过程。前一种方法使用较少的代码并不使用配置文件。后一种方式有更少的依赖性而且在调用时有更好的微控性。每种解决方案都有很多最佳适用情况。
完全使用代码写一个WCF客户端
就像一个服务终结点必须定义一个WCF的ABCs在网络上暴露服务接口,一个客户端必须知道ABCs来访问这些服务。因此,当书写代码来访问服务终结点时,ABCs被编码到客户端应用程序中。
终结点地址很简单-它是消息被发送到的地址。终结点地址格式被绑定中的传输协议定义。终结点是被终结点绑定所定义的精确的消息机制暴露出来的。WCF有一些预先设置好的绑定,比如netTcpBinding, wsHttpBinding和basicHttpBinding.契约定义了服务可以理解的精确的XML格式。典型的使用[ServiceContract]和[DataContract]标注在代码的类,接口定义中,而且WCF把类的结构序列成XML来在线上传输。
列表1.6 显示了调用服务操作的代码。代码定义了服务终结点的ABCs以便于它可以访问服务。
首先,客户端定义了它想调用的接口。这个接口的定义在客户端和服务端共享。C#语法定义与XML或是WSDL非常不同,但是语义上相同。那就是它精确的描述了如何访问服务端服务,包括操作名字和参数。然后客户端生成一个ChannelFactory类来创建一个信道,在ABCs中传输。在这种情况下,Address是被一个IIS服务器寄宿的地址,binding是BasicHttpBinding,Contract是IStockService 接口。最后,客户端生成信道来建立与服务端的通信并在服务端调用一个方法。
列表1.6 使用代码实现WCF客户端
使用代码和配置文件写一个客户端
回到2001年,Visual Studio 介绍了Add Service Reference(添加网络服务引用), 用了三个单词把分布式计算的主要保证简化到一个右键单击。这是一个好事情,因为它提供了一个入口点来扩展基于标准的分布式计算给绝大多数专业的软件开发人员。但是在使分布式计算如何容易访问时,它承担了很多重要的复杂的问题。Visual Studio 2008 为了与ASMX和其他网络服务兼容而继续支持Add Service Reference,但是也介绍使用Add Service Reference(ASR)来支持WCF。因为WCF是协议独立的而且支持一系列序列化,编码和安全方法,ASR在易于管理,性能和安全方面提供了强大的灵活性。
Visual Studio 的ASR特性用来从WCF服务获取元数据和生成代理类以及配置文件,就像在图片1.4中显示的那样。在图片背后,ASR调用svcutil.exe,svcutil.exe调用一个服务MEX终结点来查询它所有的接口来生成一个代理类和配置文件。代理类使客户端访问服务操作就像访问一个本地类的方法那样。代理类使用WCF类根据服务终结点定义的契约来编译和理解SOAP消息。配置文件存储了服务的ABCs.
写一个客户端并调用一个服务需要两个步骤: 首先,生成一个配置文件和代理类,其次,写代码使用代理类来调用服务。在Visual Studio 2008 中使用ASR,需要邮件解决方案浏览下面的添加服务引用然后在上下文菜单中选择添加服务引用。这将运行一个图片1.5中显示的那样的对话框。
对话框调用svcutil 实例来使用项目中的语言生成代码来创建代理类。同时也生成有着<system.serviceModel>节点的app.config来存储地址,绑定和必要的契约消息去调用终结点。
作为使用ASR的一个选择,你也可以直接使用svcutil.exe 实例。svcutil.exe,在C:\Program Files\Microsoft SDKs\Windows\v6.0\bin 目录下,有很多选项,而且帮助文档可以通过在命令行中输入-h 来显示。这个实例接受元数据输入而且可以产生很多形式的输出。元数据可以来自于生成类的DLL,一个WSDL文件或者通过对运行着的服务调用WS-Metadata 来获取。列表1.7显示如何使用svcutil.exe 从列表1.4和1.5定义的服务中产生元数据。
列表1.7 svcutil.exe 生成客户端代理类和配置文件
svcutil http://localhost:8000/EssentialWCF/mex -config:app.config -out:generatedProxy.cs
列表1.8 由 svcutil.exe生成的app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name ="BasicHttpBinding_StockService" closeTimeout="00:01:00" openTimeout ="00:01:00" receiveTimeout="00:01:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="624288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <security mode="None"> <transport clientCredentialType="None" proxyCredentialType="None" realm=""/> <message clientCredentialType="UserName" algorithmSuite="Default"/> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="http://localhost:8000/EssentialWCF" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_StockService" contract="StockService" name="BasicHttpBinding_StockService" /> </client> </system.serviceModel> </configuration>
在配置文件和代理类生成了以后,调用一个请求-回复服务操作是非常简单的。代理类的名字就是在服务契约的名字后面加上一个"Client"。在列表1.4和1.5中定义的服务的代理类的名字是StockServiceClient.客户端代码创建一个代理类的实例然后调用这个类中的方法。列表1.9显示了相关代码。
列表1.9 调用服务操作的客户端代码
namespace EssentialWCF { class Client { static void Main(string[] args) { StockServiceClient proxy = new StockServiceClient(); double p = proxy.GetPrice("msft"); Console.WriteLine("Price:{0}", p); proxy.Close(); } } }