WCF本质论(翻译)第一章 基础

 

1.   基础

Windows Communication Foundation(WCF)的一切都与服务(Service)有关。WCF用于创建服务、为服务提供宿主、使用服务,并确保相关服务的安全性;WCF用于实现标准和互操作性;WCF还能够有效的提高开发效率;简而言之,WCF使得分布式计算对于专业软件开发人员变得触手可及。

本章主要介绍使用WCF服务工作时需要了解的一些基本概念,关注WCF中最常用的一些特性。根据下文中的实例代码,您可以完成WCF服务的创建工作,并通过本地和网络两种方式来使用这些服务。

WCF的重要性

在深入了解WCF服务如何工作之前,有必要先理解一下WCF的重要性。那么,为什么WCF如此重要?这很容易回答,因为服务已经成为全球分布式网络的核心,而WCF是在Microsoft平台上创建和使用服务的最便捷的方式。通过WCF技术,开发人员可以将注意力集中在应用功能,而无需考虑服务通讯协议(译注:服务交互时所使用的底层通讯协议,例如:HTTPMSMQ等)。此外,如果工具能够尽可能的完成对技术细节的封装(而不是隐藏),开发人员将变得更有生产力;而WCF技术和Visual Studio 2008的组合就属于这种技术封装及其工具化的经典案例。

现代应用架构通常需要对设备、客户端软件,以及外部服务进行通盘考虑。1995年,Web网站(需要在Web服务器上部署应用,并通过向客户端浏览器发送HTML代码构造UI界面)成为业界宠儿,而现在“客户端软件+Web服务”的架构方式则逐渐成为主流。例如:iPodXBOX 360RSSAJAXMicrosoft Office、以及SharePoint3D拟真环境都采用了本地软件和Web服务相结合的方式。

对于普通应用,2008年最流行的是REST类型的Web服务接口。RESTHTTP和良好的URI方案相结合以实现基于XML数据的寻址。REST方式的数据操作通常被映射为CRUD模式,即:创建(Create)、读取(Read)、更新(Update),以及删除(Delete)。REST协议的简单性是其主要特点。

而对于商业应用,2008年最流行的是简单对象访问协议(SOAP)。SOAP能够为复杂数据交换提供健壮的模型。SOAP消息包括消息头和消息体两个部分,因此它能够实现加密和在互联网上的安全路由。如果消息是一个逻辑会话或是事务,则相关的语义可以放到消息头中,与消息一起被传递。如果有些信息需要保密,则可以对消息体进行加密,同时将安全信息放到消息头中。SOAP消息是强类型的,这使得其对开发者是友好的。和REST一样,2008年的SOAP消息主要通过HTTP通道传输,并采用文本编码方式。

WCF是协议与消息格式无关的。本书的第二章“契约”中,使用SOAP消息格式来描述服务,而第十三章“可编程Web”中,则采用REST协议来描述相同的服务。尽管二者存在着一些细微但是十分重要的差别,但是在本书中剩余的部分中,您将发现二者编程模型上共性远远大于个性。

除了通讯协议以外,要编写可靠的代码还需要可靠的软件工程实践。开发人员在为一个服务编写业务事务处理代码,或是在实现炫目的用户体现功能时,通常不会愿意直接接触底层的XML数据。为什么不呢?因为几十年的编程语言研究和编译器的设计工作已经创造出了很多非常好的工具。基于对象、类,以及组件进行开发将生成更具健壮性的代码,而像在实验室中那样直接基于操作XML中的字符串数据则无法保证这一点。

基于.NET开发应用程序的开发人员应该使用Visual Studio IDE开发环境。Visual Studio为实现WCF服务提供了相关的工具。WCF本身提供了内建模型以实现服务的寄宿,因此服务可以宿居在IIS或是Windows的托管服务中;WCF还实现了功能强大的线程管理和调节模型,对服务实例的管理极为便捷;无论是使用Singleton还是多线程模式的服务来处理并发请求,WCF的编程模型都将保持一致,开发人员无需费力处理上述细节。

WCF支持不同的消息交换模式,例如:请求/响应、单向,以及双向。如果采用网格(mesh networks and addressing)方式,则WCF可以支持P2P方式。此时,两个客户端可以自行完成通讯,而无需任何中心控制机制存在。

总之,WCF之所以重要是因为现代的应用程序已经离不开服务,而这正是WCF要解决的问题。

介绍

作为一个与服务交互的复杂的系统,WCF中定义了一组所有使用者需要熟悉以提高工作效率的专业术语。通常,定义这些术语不是为了要表达新的概念,而仅仅是为了能够让大家在讨论新技术时使用一致的方式,并理解其真实含义。

首先,服务是能够为用户提供各种功能的一组端点端点是网络上的一个可以被发送消息的资源,用户通过向端点发送消息来使用服务提供的功能;发送的消息格式要必须符合客户端和服务一致认可的契约服务将在端点中指定的地址上进行监听,并要求接收到的消息符合特定的格式要求。图1.1显示了客户端和服务之间基本关系。

1.1 客户端和服务之间的通讯过程

对于客户端来说,要完成与服务之间的有效信息通讯,必须了解所谓的“ABC”,即:地址(Address)、绑定(Binding),以及契约(Contract)。

l  A”用于表示地址,描述Where信息。地址定义了网络消息应该发送到什么地方,端点将在哪里接收消息;客户端必须将消息发送到地址指定的位置。如果使用HTTP协议,地址格式形如:http://myserver/myservice/,而对于TCP协议,地址格式则如:net.tcp://myserver:8080/myservice

l  B”用于表示绑定,描述How信息。绑定定义了端点用于通讯的信道。信道是WCF应用程序中所有消息的传输管道。一个信道由一系列的绑定元素组成。最底层的绑定元素是传输层,它负责完成消息在网络上的传递。WCF中内置的传输层包括HTTPTCP、命名管道、P2P、以及MSMQ。在其上又定义了多种用于描述安全性和事务特性的绑定元素。幸运的是,WCF中内置了许多系统自带的绑定,其中已经配置好了各种不同的信道堆栈以方便用户使用。例如:basicHttpBinding绑定实现了2007年前常见的Web服务的通讯机制,主要用于支持目前广为使用的WS-I BP 1.1规范;而wsHttpBinding绑定则实现了通用的WS-*协议,以支持消息的安全性、可靠性和事务处理。

l  C”用于表示契约,描述What信息。契约用于定义由端点提供的功能,或是特性集。契约定义了一个端点向外暴露的各种操作和操作所需要的消息格式。契约操作完成了端点实现类中各种方法的映射,包括:函数签名、输入参数,以及返回值。

如图1.2所示,多个端点共同组成了一个WCF服务,其中每个端点又由一个地址、一个绑定和一个契约所组成。因为消息流是典型的双向服务方式,因此客户端中也隐式的包含一个端点。

1.2 客户端端点和服务端点之间的通讯过程

只有当服务被寄宿于某个正在运行的操作系统进程,该服务的端点才能正常的响应消息请求。服务的宿主可以是任何进程,例如:一个无人使用的服务器进程,或是一个Web服务器,甚至是一个客户端应用程序(不论它是在桌面上全屏运行,或是最小化到Windows托盘中)。服务中定义了的各种行为特性(Behavior),用于控制服务的并发、销毁、事务处理、安全性、以及其他系统功能。服务的行为特性可以通过多种方式指定,包括:.NET属性、WCF运行过程中动态指定,或是通过外部配置文件。通过服务行为特性与灵活的服务寄宿模型相结合,可以极大的降低编写多线程代码的复杂性。

正如图1.3中所显示的,一个程序可能会实例化一个ServiceHost类用于创建多个服务端点。

为了实现服务的可发现性,一个服务可能会包含一个基础性端点,也称为元数据交换(MEX)端点。客户端可以访问MEX端点以获取WSDL格式的服务的ABC属性。当在Visual Studio 2008中使用“添加服务引用”功能,或是在设计时使用svcutil.exe工具(译注:用于生成客户端Proxy类)时,都是在使用MEX端点。当获取了MEX端点返回的WSDL响应后,会自动产生两个成果物:一个使用当前项目所用的编程语言实现的代理类(Proxy)和一个app.config配置文件。代理类对端点中的各种操作函数进行了映射,以便于客户端调用服务端点提供的各种功能。代理类的接口与服务定义时的函数签名并不是完全一致,但是代理类必须保证客户端传递给服务的消息必须与服务契约中描述的完全一致;而app.config文件则包含了服务绑定的具体配置信息。

1.3 服务寄宿过程

实现一个WCF服务

本节用于描述如何使用WCF实现一个简单的服务。为了简化,我们假设采用HTTP协议作为底层的通讯协议,假设使用文本格式来传输XML文档,假设不需要考虑安全性(把它们放到应用中处理),假设使用同步的请求/响应会话方式,并且假设我们的服务只支持一个操作。该操作接收一个字符串类型的输入参数,并返回一个double类型的返回值。在后面的章节中,我们将逐渐更改上面的假设条件,但是现在,我们需要抛弃一切不必要的复杂性。

就是ABC

要定义端点,只需要指定其ABC,即:地址、绑定,以及契约。在代码清单1.11.3中,完成了下面的ABC定义:

l  A”用于表示地址,描述Where信息。本服务的请求监听地址为:http://localhost:8000/EssentialWCF

l  B”用于表示绑定,描述How信息。本示例中使用basicHttpBinding,它将告知WCF使用WS-I BP 1.1规范(Web Service的通用协议)进行通讯。

l  C”用于表示契约,描述What信息。它主要描述了服务都能够响应哪些操作,并且这些操作需要的输入和输出数据的消息格式。在本例中,服务的契约在StockService类中进行定义。

在本节中,我们将使用两种不同的方式来实现上述服务。一种方式是完全使用代码定义端点的ABC属性,这种方式不需要依赖于外部的配置文件;另一种方式是使用配置文件来定义端点的ABC属性,这种方式需要的代码更少,但是需要依赖于额外的配置文件才能运行,这增加了系统复杂性。实际上后一种使用配置文件的方式更为常用,虽然它增加了复杂性,但是却提供了更好的灵活性。这种灵活性的根源在于:在配置文件中定义的端点的ABC属性可以由系统管理员来修改;而如果在代码中定义端点的ABC属性,则只有程序员自己可以修改。

完全使用代码编写一个WCF服务

以高度抽象的观点来看,如果不考虑内部实现机制,则编写一个WCF服务与编写任何其他形式的服务几乎没有区别。首先,需要编写一些代码来实现服务的基本功能;然后,需要把上述代码寄宿在一个操作系统进程中;最后,由宿主进程完成服务请求的监听和应答处理。WCF实际上只是对上述操作进行了进一步的规范,以使得开发人员能够方便完成上述工作,并确保在每个步骤都不会出错。例如:通过使用系统内置的绑定和编码器,WCF服务能够基于标准的SOAP消息进行通讯。WCF在无需开发人员介入的情况下,就实现了线程管理、并发控制、服务实例管理等功能,并确保其行为的确定性。

要实现一个WCF服务,需要定义一个类,并在类上指定一些特殊属性。这些属性是在System.ServiceModel命名空间中定义的。System.ServiceModel命名空间是.NET 3.0组件中的一部分,它实现了WCF服务中的许多重要功能。当代码被编译时,CLR会对上述特殊属性进行解释,并将它们替换成对应的运行时代码。属性对于.NET平台并不新鲜,早在.NET 1.0时代就已经出现了。就像.NET 1.0.NET 1.1,以及.NET 2.0中的ASMX(译注:.NET平台上用于实现Web服务,Web服务中的每个函数上都会使用WebMethod属性进行标记)一样,WCF也使用属性帮助开发人员提高WCF服务的开发效率。

代码清单1.1中显示了寄宿在一个控制台程序中的WCF服务的所有代码。在这个例子中,我们完成了下面一些工作:

l  定义契约:编写了一个具有实用功能的.NET类,并为其指定了相关的WCF属性。[ServiceContract]属性用于将类标记为契约。如果采用WSDL标准中的表述方式,则是:[ServiceContract]定义了一个端口类型(PortType)。[OperationContract]属性用于定义类中可以被服务接口调用的方法。同时,它也定义了该方法的输入输出消息。同样,采用WSDL标准中的表述方式,则为:[OperationContract]定义了操作和消息。代码清单1.11.3中定义了一个名为StockService的类,该类只有一个名为GetPrice的方法。

 

注意:

本书中的例子中的接口通常都很简单,其输入输出多为字符串和数值这种简单类型的数据。而在实际应用中,服务操作通常需要接收并返回复杂类型的数据。网络通讯的方式应该是“通讯次数少,且单次通讯信息量大”而非“通讯次数多,且单次通讯信息量小”,以降低通讯负载,并缩短响应延迟。这就要求每次服务调用中应该传递更多信息,即:使用复杂数据类型作为输入参数和返回值。

 

l  定义端点:在端点定义中,我们通过调用ServiceHost类中的AddServiceEndpoint方法来描述端点的地址、绑定,以及契约属性。其中:端点的地址为空,表明端点的地址和服务的基础地址相同;绑定则使用了兼容WS-I BP 1.1且能够与大多数实现了XML Web Service的系统进行互操作的basicHttpBindingWS-I,或者称为Web服务互操作,是MicrosoftIBMBEAOracle、以及其它一些主要的系统提供商共同讨论确定并公开发布的兼容性级别。WS-I规范并没有定义一个标准,而是提供了一个指南和一些工具来鉴别软件是否与现有的一些标准兼容,例如:HTTPXML

l  将服务寄宿到进程中,使之能够监听服务请求:代码清单1.11.3中都使用ServiceHost类将WCF服务寄宿到了一个控制台程序中。WCF服务在http://localhost:8000/EssentialWCF处进行监听。

 

代码清单1.1 完全使用代码实现一个WCF服务

using System;

using System.ServiceModel;

 

namespace EssentialWCF

{

  [ServiceContract]

  public interface IStockService

{

  [OperationContract]

  double GetPrice(string ticker);

}

 

public class StockService : IStockService

{

  public double GetPrice(string ticker)

{

  return 94.85;

}

}

 

public class Service

{

  public static void Main()

{

  ServiceHost serviceHost = new ServiceHost(typeof(StockService),

new Uri("http://localhost:8000/EssentialWCF"));

      serviceHost.AddServiceEndpoint(typeof(IStockService), new BasicHttpBinding(), "");

serviceHost.Open();

 

Console.WriteLine("Press <Enter> to terminate.\n\n")

Console.ReadLine();

 

serviceHost.Close();

}

}

}

代码和配置文件相结合编写一个WCF服务

WCF为在配置文件中定义服务属性提供了强大的支持。开发人员只需要在代码中完成服务中需要实现的各种功能特性或是算法实现,而服务端点的地址、绑定,以及服务行为特性的定义都可以转移到配置文件中完成。

在配置文件中定义端点属性和行为特性信息与在编码中直接定义这些属性相比更具灵活性。例如:假设当前服务端点使用HTTP协议与客户端进行通讯,在代码清单1.1中可以看到,这需要使用BasicHttpBinding类,并通过AddServiceEndpoint方法指定上述绑定信息。但是如果出现需求变更,要求采用WsHttpBinding来实现传输层安全的基础上额外的消息层安全,则上述硬编码的实现方式可以需要更改代码并对代码重新编译。而如果将服务绑定属性的定义移动到配置文件中来完成,则无需重新编译就可以完成功能变更。此外,如果该服务契约需要同时支持两种通讯协议,则可以在配置文件中同时定义两个服务端点:一个端点用于支持HTTP协议,而另一个端点用于支持WS-Security协议,这些更改都不需要变更代码就可以完成。采用配置文件可以增强代码的可管理性。

代码清单1.2中显示了寄宿在一个控制台程序中的WCF服务的完整代码。这段代码需要与一个配置文件结合起来使用,配置文件中定义了服务端点属性和服务的各种行为特性。在本例中,完成了下面一些工作:

l  定义契约:编写了一个具有实用功能的.NET类,并为其指定了相关的WCF属性。无论使用纯代码方式还是使用代码和配置文件相结合方式,在这里的服务契约定义都是完全一样的。与代码清单1.1中相同,代码清单1.2中也定义了一个名为StockService的类。

l  将服务寄宿到一个操作系统进程中,以允许客户端通过网络进行访问:与代码清单1.1中相同,需要创建一个在System.ServiceModel命名空间中定义的ServiceHost对象,并调用其Open方法。

l  定义一个配置文件用于描述服务基础地址以及服务端点的ABC属性:需要注意的是代码清单1.2中并没有显式的引用配置文件。实际上,当ServiceHost.Open方法被调用的时候,WCF会在当前程序集的配置文件(app.config或是web.config)中寻找<serviceModel>配置项,并应用其中的配置数据。

 

代码清单1.2 代码和配置文件相结合实现一个WCF服务

using System;

using System.ServiceModel;

 

namespace EssentialWCF

{

  [ServiceContract]

  public interface IStockService

{

  [OperationContract]

  double GetPrice(string ticker);

}

 

public class StockService : IStockService

{

  public double GetPrice(string ticker)

{

  return 94.85;

}

}

 

public class Service

{

  public static void Main()

{

  ServiceHost serviceHost = new ServiceHost(typeof(StockService));

serviceHost.Open();

 

Console.WriteLine("Press <Enter> to terminate.\n\n")

Console.ReadLine();

 

serviceHost.Close();

}

}

}

 

代码清单1.3中显示了与代码清单1.2中代码协同工作的配置文件的完整内容。在ServiceModel节中定义了服务端点;每个服务端点上,定义了地址、绑定,以及契约信息。其中:服务端点的地址信息为空,表示该服务端点的地址与服务的基础地址相同。如果存在多个端点,则每个服务端点的地址必须唯一。本例中使用的绑定为basicHttpBinding,而契约名称则为源码中定义的StockService类的全名:EssentialWCF.StockService

 

代码清单1.3 代码和配置文件相结合实现一个WCF服务中的配置文件

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<services>

<service name="EssentialWCF.StockService">

<host>

<baseAddresses>

<add baseAddress="http://localhost:8000/EssentialWCF" />

</baseAddresses>

</host>

<endpoint address=""

binding="basicHttpBinding"

contract="EssentialWCF.IStockService" />

</service>

</services>

</system.serviceModel>

</configuration>

 

进一步了解配置文件

根据服务寄宿方式的不同,服务配置文件可能是web.config或是app.config。服务配置文件中必须包含<system.serviceModel>节点。<system.serviceModel>节点能够描述服务数量、绑定方式、服务行为特性、客户端信息、诊断方式、扩展组件、寄宿环境,以及COM+互操作等配置信息。<system.serviceModel>节点中至少需要定义<services>节点。<services>中能够定义多个服务的端点信息,每个服务中至少包含一个<endpoint>节点,用于描述服务端点的ABC属性。

<endpoint>节点的address属性定义了客户端向端点发送消息的URI地址。例如:如果一个服务正在使用基于HTTP协议的basicHttpBinding绑定,则URI地址形如http://www.myserver.com:8080/MyService/。如果address属性中为绝对路径(不是空,也不是相对路径),则该绝对路径将覆盖创建服务时在host定义的基础地址(译注:使用相对路径时,基础地址才有效)。通常,当宿主进程启动WCF服务时,WCF服务会在address属性描述的地址上(相对路径或是绝对路径)监听用户请求。而当采用IIS寄宿方式时,监听器可能已经启动了(译注:IIS负责维护监听器),因此WCF服务只需要在这个地址上进行注册,然后IIS会把在对应URI地址上的请求转发给寄宿的WCF服务来处理。

<endpoint>节点的binding属性定义了服务连接的通讯细节,它负责定义整个信道堆栈。信道堆栈中至少要包含一个网络适配器信道,此外,还可以包含加密信道和压缩信道等其他信道。WCF中内置了许多预定义绑定实现,例如:BasicHttpBinding绑定(与ASMX兼容)、WSHttpBinding(实现了许多高级Web服务特性,包括:消息级别的安全性、事务处理,以及其他高级特性),以及NetTcpBinding(实现了一种类似于.NET Remoting或是DCOM的安全高速的通讯方式)。

<endpoint>节点的contract属性定义了该服务端点的契约类型。如果服务包含MEX端点,则WCF会检查该类型,并通过MEX端点对外暴露其元数据。此时,WCF会在多个地点查找上述契约类型的定义。首先,会在\bin目录下进行查找;此外,还会在本机的全局程序集缓存(GAC)中进行查找。如果WCF在上述两个位置都无法找到契约类型的定义,则当使用“添加服务引用”或是使用svcutil.exe工具请求该服务的WSDL信息时,服务会返回错误信息;而如果服务中不包含MEX端点,则此时服务仍能正常工作,但是客户端却无法正常获得服务端点的ABC属性。

进一步了解服务寄宿

WCF允许将服务寄宿在任意的操作系统进程中。在大多数情况下,IIS是一个理想的WCF服务寄宿环境,因为IIS能够提供优异的性能、可管理性,以及安全性。如果已经有正在运行的IIS,则系统中应该已经配置好了恰当的安全设置。通常,高端的电子商务网站可能会额外定义一些安全策略和规程,并配备自动化工具以验证其符合度;而规模较小的购物网站则经常使用IISWindows 2003中内置的默认安全性设置。不论是哪种情况,IIS中的安全性设置都将同样应用到寄宿于IIS中的WCF服务上。

虽然IIS有这样那样的优势,但是偶尔也会有不使用IIS作为寄宿环境的情况,比如:我们可能不想使用HTTP作为底层通讯协议;或是我们想要显示控制服务的启动和停止事件;又或者是我们想提供一个自定义的管理接口而不是使用IIS自带的管理工具。如果不使用IIS,也没有任何问题。WCF为“自寄宿”方式提供了简便而灵活的支持。所谓“自寄宿”是指开发人员自己实例化ServiceHost类来完成服务的寄宿,而非使用IIS或是Windows进程激活服务(WAS)。

寄宿WCF服务最简单的方法是编写一个如代码清单1.1中的控制台程序。尽管这种方式在实际的生产环境中用处不大,因为在服务器上开一个Windows命令行窗口不是一种好的实践方式;但是以入门为目的,这种方式至少解除了WCF服务对于IIS运行环境的依赖性。在代码清单1.1中,主程序先创建了一个ServiceHost类的新实例用于寄宿实际的WCF服务;然后,调用了ServiceHost类中的Open方法以启动WCF服务;最后,主程序将单纯的等待用户按下“Enter”键,以便调用ServiceHost类中的Close方法来结束服务监听过程。

Open方法被调用后,ServiceHost将在服务端点中指定的地址上进行监听。当收到请求消息时,ServiceHost会执行如下一些操作:首先,根据在绑定的定义的信道堆栈执行对应的解密、解压缩,以及安全检查操作;其次,根据契约将输入消息反序列化成.NET中的对象,并调用适当的对象方法。

暴露元数据交换(MEX)端点

WCF中的元数据是指“详细描述如何与服务进行通讯的信息”。客户端可以向一个正在运行的服务发起元数据查询请求,以了解该服务的端点以及端点所需的消息格式。在设计阶段,客户端应该依据WS-MetadataExchange标准向服务发送请求信息,并接收WSDL返回值。该WSDL返回值将被用于定义客户端代理类和配置文件,并在随后的运行阶段中完成与服务的通讯。图1.4显示了上述交互过程。

 

1.4 通过MEX端点获取元数据

 

在默认情况下,WCF服务并不会向外暴露MEX端点。这意味着无法查询服务的元数据并获取与服务通讯的方式。而如果不知道地址、绑定,以及契约等信息,则很难完成与服务的通讯,除非将服务的详细信息放在注册表中。幸运的是,利用WCF向外暴露MEX端点十分的简单,这使得客户端可以与服务进行正常的通讯。要想外暴露MEX端点既可以通过纯编码方式实现,也可以通过配置文件方式实现。

代码清单1.4中显示了在服务中暴露MEX端点的必要代码。这个例子对代码清单1.1中的例子进行了一些扩展:首先,在服务中增加了一个服务行为特性用于告知WCF在服务中增加一个MEX契约-IMetadataExchange;其次,在服务中增加了一个用于IMetadataExchange契约的端点,端点的传输协议为HTTP,地址为“mex”。这里的地址采用的是相对路径,因此服务的基础地址将作为该MEX端点地址的前缀,于是MEX端点的完整地址应该是http://localhost:8000/EssentialWCF/mex;此外,这个例子中还对服务的行为特性进行了修改,允许在MEX端点上使用HTTP GET。虽然不是必须的,但支持HTTP GET将允许用户通过浏览器直接访问MEX端点。

 

代码清单1.4 通过代码对外暴露MEX端点

using System;

using System.ServiceModel;

using System.ServiceModel.Description;

 

namespace EssentialWCF

{

[ServiceContract]

public interface IStockService

{

  [OperationContract]

  double GetPrice(string ticker);

}

 

public class StockService : IStockService

{

  public double GetPrice(string ticker)

{

  return 94.85;

}

}

 

public class Service

{

  public static void Main()

{

  ServiceHost serviceHost = new ServiceHost(typeof(StockService),

new Uri("http://localhost:8000/EssentialWCF"));

serviceHost.AddServiceEndpoint(

typeof(IStockService),

new BasicHttpBinding(),

"");

ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();

behavior.HttpGetEnabled = true;

serviceHost.Description.Behaviors.Add(behavior);

 

serviceHost.AddServiceEndpoint(

typeof(IMetadataExchange),

MetadataExchangeBindings.CreateMexHttpBinding(),

"mex");

serviceHost.Open();

 

Console.WriteLine("Press <Enter> to terminate.\n\n")

Console.ReadLine();

 

serviceHost.Close();

}

}

}

 

如果决定使用配置文件而不是代码来描述端点属性,则也可以在配置文件中完成MEX端点的对外暴露。代码清单1.5在代码清单1.3基础上进行了改进,增加了对MEX端点的支持。在新配置文件中,service节点中增加了MEX端点配置项,同时还增加了服务行为特性配置项,以支持通过HTTP直接访问MEX端点。

 

代码清单1.5 通过配置文件对外暴露MEX端点

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<services>

<service name="EssentialWCF.StockService"

behaviorConfiguration="myServiceBehavior">

<host>

<baseAddresses>

<add baseAddress="http://localhost:8000/EssentialWCF" />

</baseAddresses>

</host>

 

<endpoint address=""

binding="basicHttpBinding"

contract="EssentialWCF.IStockService" />

<endpoint address="mex"

binding="mexHttpBinding"

contract="IMetadataExchange" />

</service>

</services>

 

<behaviors>

<serviceBehaviros>

<behavior name="myServiceBehavior">

<serviceMetadata httpGetEnabled="True"/>

</behavior>

</serviceBehaviros>

</behaviors>

 

</system.serviceModel>

</configuration>

 

实现一个WCF客户端

WCF为客户端与服务通讯提供了丰富的API接口。这些API接口在System.ServiceModel命名空间中实现,能够完成“.NET类型到XML格式数据的序列化”功能和“从客户端到服务器的消息发送”功能。开发人员既可以直接使用这些基础API接口,也可以利用工具来自动生成客户端代理类和对应的配置文件。在本节中,我们首先来演示如何直接通过代码来调用服务,随后,我们再利用工具生成的代理类来完成相同的操作。其中,前者所需的代码更少且不依赖于外部的配置文件;而后者虽然需要依赖配置文件,但是却能够实现对服务调用的细粒度控制。这两种方式各有其适用场景。

完全使用代码编写一个WCF客户端

正如服务端点必须定义其ABC属性用以在网络上暴露其服务功能,客户端也必须知道上述服务的ABC属性,以实现对服务功能的访问。因此,当编写访问服务端点的代码时,必须在客户端代码中描述相关服务的ABC属性。

在服务端点的ABC属性中,地址属性(译注:A属性)比较容易描述,它就是一个发送消息的网络地址,其地址格式由绑定中所使用的传输层协议来定义;绑定属性(译注:B属性)定义了客户端和服务之间实际使用的通讯方式。WCF中内置了一组预定义好的绑定,例如:netTcpBindingwsHttpBinding,以及basicHttpBinding;契约属性(译注:C属性)精确定义了服务能够理解的XML数据格式。在代码中,通常需要在类或是接口上标记[ServiceContract][DataContract] 属性标识,而WCF则根据上述标识完成类结构的序列化操作,并在底层传输序列化后的XML格式数据。

代码清单1.6中显示了如何调用服务操作的相关代码。代码中描述了服务端点的ABC属性,因此能够完成服务操作的调用。

首先,客户端需要定义服务接口(译注:使用C#语言定义一个等价的服务接口),即:服务接口的定义在服务和客户端之间是共享的。尽管从语法上来说,使用客户端使用C#语言定义的服务接口与MEX端点中返回的使用XML或是WSDL描述的服务契约定义是不同的;但是,从语义上来看,二者含义相同。因为,使用C#语言定义的服务接口同样精确描述了服务的各种功能,包括服务操作的名称以及操作参数信息;接下来,客户端需要创建一个ChannelFactory类。如果给定服务端点的ABC属性,ChannelFactory能够创建一个信道。在本例中,地址属性使用一个IIS服务器能够处理的URL地址,底层的绑定使用BasicHttpBinding类,契约属性设置为IStockService接口;最后,客户端将创建一个与服务通讯的信道,并调用服务中的一个方法。

 

代码清单1.6 完全使用代码实现WCF客户端

using System;

using System.ServiceModel;

 

namespace Client

{

[ServiceContract]

public interface IStockService

{

  [OperationContract]

  double GetPrice(string ticker);

}

 

class Client

{

  static void Main()

{

ChannelFactory<IStockService> myChannelFactory =

new ChannelFactory<IStockService>(

new BasicHttpBinding(),

new EndpointAddress

("http://localhost:8000/EssentialWCF"));

 

IStockService wcfClient = myChannelFactory.CreateChannel();

 

double p = wcfClient.GetPrice("msft");

Console.WriteLine("Price:{0}", p);

}

}

}

 

代码和配置文件相结合编写一个WCF客户端

回首2001年,Visual Studio中第一次引入了“添加Web引用”功能,它将分布式计算中的一个主要工作简化为右键单击操作。这个功能很棒,因为它引领许多专业开发人员进入了可扩展的、基于标准的分布式计算领域。但是在这个功能大幅简化使用分布式计算功能的同时也隐藏了许多重要的复杂性。Visual Studio 2008中仍然提供原有的“添加Web引用”功能用于支持原有的ASMX以及其他Web服务;此外,Visual Studio 2008中还引入了一个名为“添加服务引用”(ASR)的新功能用于支持新增的WCF服务。ASR能够支持WCF服务的协议无关性,并支持不同的序列化和编码方式以及多种安全机制,提供了非常灵活的可管理性、性能,以及安全性。

如图1.4所示,Visual Studio中的ASR功能可以从WCF服务中获取元数据信息,并生成代理类和对应的配置文件。在后台,ASR实际上调用了svcutil.exe,该工具会调用服务的MEX端点来查询其接口信息并生成一个代理类和配置文件。代理类根据服务端点中的定义的契约,使用WCF中的辅助类构建并解析SOAP消息,使得客户端可以像调用本地方法一样访问远程服务操作。而配置文件中则保存了服务的ABC属性。

要编写一个客户端来调用服务需要执行两个步骤:首先,生成代理类和配置文件;其次,编写代码使用代理类调用服务操作。如果使用Visual Studio 2008中的ASR功能生成代理类和配置文件,可以右键单击解决方案管理器中的服务引用节点,并选择其上下文菜单中的“添加服务引用”功能。该操作将打开一个对话框窗口,如图1.5所示。

这个对话框会调用svcutil.exe工具,根据当前项目所使用的编程语言创建实现代理类的源代码文件;此外,他还会创建一个名为app.config的配置文件。配置文件中包含<system.serviceModel>节点,存储了调用服务端点所需的地址、绑定,以及契约信息。

如果不使用ASR,还可以直接使用svcutil.exe工具。可以在C:\Programming Files\Microsoft SDKs\Windows\v6.0\Bin目录中找到该工具,它支持多个命令行参数,可以使用­-h命令行参数查看其帮助信息。这个工具接收元数据作为输入,并生成各种不同形式的输出。元数据信息的数据来源可以是包含服务实现类的DLL,可以是一个WSDL文件,还可以是向正在运行中的服务发出WS-Metadata调用后获得的WSDL返回值。代码清单1.7中显示了如何使用svcutil.exe工具来为代码清单1.41.5中定义的服务生成元数据。

 

1.5 使用Visual Studio生成代理类和配置文件

 

代码清单1.7 使用svcutil.exe工具生成代理类和配置文件

svcutil.exe http://localhost:8000/EssentialWCF/mex/

-config:app.config

-out:generatedProxy.cs

 

无论使用哪种方式生成的代理类和配置文件,结果都是一样的。代码清单1.8中显示了所生成的配置文件。需要注意的是客户端生成的配置文件要比服务自身的配置文件(参考代码清单1.3)更加复杂详细,这为客户端提供了更大的灵活性。客户端可以更改一些特定的配置属性,例如:连接超时时间、缓冲区大小,以及客户端支持的安全性身份鉴别。

 

 

代码清单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:10:00" sendTimeout="00:01:00"

allowCookies="false" bypassProxyOnLocal="false"

hostNameComparisonMode="StrongWildcard"

maxBufferSize="65536" maxBufferPoolSize="524288"

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中定义的服务“StockService”的客户端代理类的名称为“StockServiceClient”。生成配置文件和代理类后,调用一个“请求/响应”方式的服务操作十分简单,只需要在客户端代码中创建一个代理类的实例,并调用该类的方法就可以了。代码清单1.9中展示了如何执行上述操作。

 

代码清单1.9 用于调用服务操作的客户端代码

using System;

using System.ServiceModel;

 

namespace EssentialWCF

{

class Client

{

 static void Main()

{

StockServiceClient proxy = new StockServiceClient();

double p = proxy.GetPrice("msft");

Console.WriteLine("Price:{0}", p);

proxy.Close();

}

}

}

 

IIS服务寄宿

WCF服务可以寄宿在操作系统中运行的任何托管进程中。尽管WCF类库中提供了许多API接口以允许服务了解其寄宿环境,但是服务通常不需要知道,也会不关心它的实际宿主。WCF服务的寄宿地点可以是一个Windows服务,该服务在计算器启动时自动启动,在计算机关闭时自动关闭;也可以是一个最小化到系统托盘的客户端应用程序。但通常,WCF服务会选择IIS作为其宿主。

讨论

IIS非常适合WCF服务的宿主。IIS完全基于Windows平台构建,关IIS管理、安全性,以及应用开发的各种资料非常多。IIS具有高可扩展性和高可靠性,并能够通过配置实现高安全性,能够为WCF服务提供良好的寄宿环境。在WCF出现之前,通过IIS发布Web服务主要采用ASMX技术。在此基础上,WCF.NET 3.5中替代ASMX,成为通过IIS发布Web服务的首选方案。

接下来,再来看看采用IIS作为宿主对WCFABC属性(地址、绑定,以及契约)有何影响。首先,服务地址将由包含服务文件的IIS虚拟目录来确定;其次,服务绑定的底层传输协议必须是HTTP/S协议,因为IIS只支持该协议。因此,尽管任何基于HTTP协议的绑定都能够用于IIS寄宿,但是WCF中内置的绑定类中只有basicHttpBindingwsHttpBinding符合要求;最后,IIS寄宿环境对服务契约(服务端点的SOAP定义)没有任何特殊限制,所以做任何特殊处理。

获取WCF服务元数据(采用WSDL格式描述)的方式与ASMX相同,都是通过在服务地址后面加上WSDL参数的方式,(例如:http://localhost/myservice.svc?WSDL)。当IIS接受到上述请求后,它将调用WCF服务的MEX端点,并以WSDL格式返回结果。而与ASMX不同的是,在默认情况下,WCF服务不发布MEX端点,因此无法响应由Visual Studio 2008的“添加服务引用”功能或是svcutil.exe工具发出的元数据请求。要使用MEX端点,必须在代码(参见代码清单1.4)或是配置文件(参见代码清单1.5)中显示的启用MEX端点。

IIS寄宿三步曲

要实现WCF服务在IIS中的寄宿需要执行以下三个步骤:

IIS中创建一个虚拟应用程序来保存服务;

创建一个SVC文件来定义服务实现;

web.config文件中增加<system.serviceModel>节点;

定义一个IIS虚拟应用程序

IIS虚拟应用程序由应用程序池和虚拟目录组成。当WCF采用IIS寄宿方式时,应用程序池负责创建ServiceHost实例;而虚拟目录用于存储服务文件,包括:SVC文件、web.config文件,以及编译后的Dll文件。

创建一个SVC文件

SVC文件可以引用服务实现类。SVC文件既可以用普通的文本编辑器创建,也可以用Visual Studio创建。通常,服务的实现类位于一个Dll中,并在SVC文件进行引用。实现服务的Dll必须放在虚拟目录的/bin目录下,或是在GAC中进行注册。代码清单1.10中显示了一个引用了编译后.NET类的SVC文件。

 

代码清单1.10 引用编译后的服务的SVC文件

<%@ServiceHost Service="EssentialWCF.StockService" %>

 

SVC文件还可以定义服务实现的具体内容。此时,SVC文件中将包含更多的代码,但好处是减少了外部依赖(译注:引用方式的SVC文件要依赖于外部的服务实现Dll)。由于服务实现的源代码直接部署在IIS服务器上,因此系统的操作人员或是支持人员可以直接修改代码,而不需要启动开发工具并重新编译。这种方式的风险和好处都很明显。它的风险包括知识产权失去保护和代码变更管理失去控制,因为每台服务器上的代码都是明文保存的,且可以任意修改。此外,这种方式还将影响系统的性能(译注:因为源代码需要在第一次使用时进行JIT编译);而这种方式的好处则包括代码透明性和错误及时更正,因为在这种情况下,用户能够清楚的知道代码的实际运行过程,以及在需要的时候如何修改相关代码。代码清单1.11中显示了一个包含具体服务实现的SVC文件,其中的代码将在服务第一次被调用时编译。

 

代码清单1.11 引用编译后的服务的SVC文件

<%@ServiceHost Language="C#" Service="EssentialWCF.StockService" %>

using System;

using System.ServiceModel;

 

namespace EssentialWCF

{

[ServiceContract]

public interface IStockService

{

[OperationContract]

double GetPrice(string ticker);

}

 

public class StockService : IStockService

{

public double GetPrice(string ticker)

{

return 94.85;

}

}

}

 

配置web.config中的<system.serviceModel>节点

因为使用IIS寄宿,因此WCF服务端点只能在配置文件中定义,而不能在代码中定义。端点的配置信息保存在web.config文件的<system.serviceModel>节点中。与其他寄宿方式一样,配置信息中必须定义端点的ABC属性,包括:地址、绑定,和契约。代码清单 1.12中显示了一个采用IIS服务寄宿方式的web.config文件。需要注意的是配置文件中<system.serviceModel>节点中的内容与代码清单1.5中的内容完全相同。

服务的地址由SVC文件所在的虚拟目录地址确定。如果服务中只有一个端点,则端点的地址可以是空,这意味着端点地址与服务地址相同;如果服务中存在多个端点,则每个端点应该定义一个相对地址。

服务绑定的信道堆栈必须使用HTTP协议作为传输层协议。WCF中内置的绑定中有两个符合要求,分别是:basicHttpBindingwsHttpBinding;而其它自定义绑定实现类,只要使用HTTP作为底层的传输层协议,也可以支持;有关用户自定义绑定类的内容将在第三章和第四章中详细描述。

端点契约定义了实现服务的具体类。该服务实现类的运行时代码必须是服务可访问的,即:它或者位于/bin目录或是GAC中的Dll程序集中,或者内嵌在SVC文件中。

 

代码清单1.12 定义服务的web.config文件

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

 

<system.serviceModel>

<services>

<service name="EssentialWCF.StockService"

behaviorConfiguration="MEXServiceTypeBehavior" >

<endpoint address=""

binding="basicHttpBinding"

contract="EssentialWCF.IStockService" />

<endpoint address="mex"

binding="mexHttpBinding"

contract="IMetadataExchange" />

</service>

</services>

 

<behaviors>

<serviceBehaviors>

<behavior name="MEXServiceTypeBehavior">

<serviceMetadata httpEnabled="true" />

</behavior>

</serviceBehaviors>

</behaviors>

</system.serviceModel>

 

</configuration>

 

实现一个ASMX服务的WCF客户端

WCF客户端可以调用任意标准服务,而无需关心目标主机的系统环境。WCF客户端与.NET 1.1框架中的Web服务(或者称为ASMX)完全兼容。WS-I BP 1.1标准保证了WCF客户端能够正常调用基于ASMXWeb服务。

工具支持

与调用WCF服务相同,也可以通过“添加服务引用”(ASR)或是svcutil.exe来创建访问ASMX服务所需的代理类和配置文件。在生成上述代理类和配置文件后,客户端可以通过实例化一个代理类来与基于ASMXWeb服务进行通讯,并调用相关的方法。此外,也可以继续使用原有的“添加Web引用”(AWR)或是wsdl.exe来创建代理类和配置文件。同样,在生成了代理类和配置文件后,客户端也是通过调用代理类的方法来完成与服务之间的通讯。

如果是一个新开发的应用程序需要调用已有的基于ASMXWeb服务,则应该使用新的ASR/svcutil.exe方式;而如果是一个已有的应用程序,且其中已经用AWR或是wsdl.exe创建了代理类和配置文件,则应该继续使用原有的AWR/wsdl.exe方式。这主要是为了确保客户端在与ASMX服务通讯时,不会创建两个不同类型的代理类和配置文件。此外,即使原有的客户端需要升级以调用新的使用basicHttpBinding绑定的WCF服务,仍然可以使用AWR/wsdl.exe方式来生成访问WCF服务的代理类。

 

1.1 选择生成代理类和配置文件的方式

 

ASMX服务

WCF服务

修改一个已经引用了ASMX服务的客户端

使用“添加Web引用”,或是wsdl.exe

使用“添加Web引用”,或是wsdl.exe

新开发一个需要访问ASMX服务的客户端

使用“添加服务引用”,或是svcutil.exe

使用“添加服务引用”,或是svcutil.exe

 

无论是使用svcutil.exe还是使用wsdl.exe,客户端代码都需要使用生成的代理类来访问远程服务,而且其代理类的配置信息也都在app.config中进行描述。

生成客户端代理类和配置文件

如果是正在修改一个已经存在且生成了ASMX代理类的客户端应用程序,则应该使用“添加Web引用”方式。代码清单1.13中显示了使用AWR代理类调用服务操作的客户端代码。

 

代码清单1.13 使用AWR代理类访问ASMX服务的客户端代码

using System;

namespace Client

{

class Program

{

static void Main(string[] args)

{

ASMXReference.StockService proxy =

new ASMXReference.StockService();

double p = proxy.GetPrice("msft");

Console.WriteLine("Price: {0}", p);

proxy.Close();

}

}

}

 

代码清单1.14中显示了利用Visual Studio中的“添加Web引用”功能生成的配置文件。需要注意的是app.config中只包含了服务的地址。这种极端简陋的配置信息与通过“添加服务引用”功能生成的app.config文件(参考代码清单1.16)中极端详细的配置信息形成了巨大的反差。利用ASR生成的额外配置信息允许开发人员或是系统管理员在不修改代码的情况下诸如超时时间等配置参数。

 

代码清单1.14 使用AWR生成的用于访问ASMX服务的app.config文件

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<configSections>

<sectionGroup name="applicationSettings"

type="System.Configuration.ApplicationSettingsGroup,

System, Version=1.0.0.0, Culture=netural,

PublicKeyToken=b77a5c561934e089" >

<section name="Client.Properties.Settings"

type="System.Configuration.ClientSettingSection,

System, Version=1.0.0.0, Culture=netural,

PublicKeyToken= b77a5c561934e089"

requirePermissioin="false" />

</sectionGroup>

</configSections>

<applicationSettings>

<Client.Properties.Settings>

<setting name="Client_ASMXReference_StockService"

serializeAs="String">

<value>http://localhost/asmx/service.asmx</value>

</setting>

</Client.Properties.Settings>

</applicationSettings>

</configuration>

 

如果是在创建一个全新的、没有生成过ASMX服务代理类的客户端应用程序,则应该使用“添加服务引用”方式,以在新项目中使用新的代理类。代码清单1.15中显示了使用ASR生成的代理类来访问ASMX服务的客户端代码。需要注意的是,在创建代理类实例时,必须指定服务端点的名称:StockServiceSoap。这是因为“添加服务引用”功能在app.config中添加了两个端点的配置信息,其中一个使用了basicHttpBinding绑定,而另一个则使用了与SOAP 1.1规范兼容的自定义绑定。

 

代码清单1.15 使用ASR代理类访问ASMX服务的客户端代码

using System;

namespace Client

{

class Program

{

static void Main(string[] args)

{

using(WCFReference.StockServiceSoapClient proxy =

new WCFReference.StockServiceSoapClient

("StockServiceSoap"))

{

double p = proxy.GetPrice("msft");

Console.WriteLine("Price:{0}", p);

}

}

}

}

 

代码清单1.16中显示了利用Visual Studio中的“添加服务引用”功能生成的配置文件。需要注意的是,ASMX服务的所有绑定和端点的详细信息都被导出并保存在app.config文件中。此外,配置文件中还定义了两个端点,其中:一个端点是StockServiceSoap,它使用basicHttpBinding绑定,并遵循WS-I BP 1.1规范;另一个端点是StockServiceSoap12,它使用一个用户自定义绑定,该绑定遵循较新的SOAP 1.2规范。因为ASMXWS-I BP 1.1兼容的,所以在上面的代码中使用了基于SOAP 1.1规范的StockServiceSoap端点。

 

代码清单1.16 使用ASR生成的用于访问ASMX服务的app.config文件

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<bindings>

<basicHttpBinding>

<binding name="StockServiceSoap" closeTimeout="00:01:00"

openTimeout="00:01:00" receiveTimeout="00:10:00"

sendTimeout="00:01:00" allowCookies="false"

bypassProxyOnLocal="false"

hostNameComparisonMode="StrongWildcard"

maxBufferSize="65536" maxBufferPoolSize="524288"

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>

<customBinding>

<binding name="StockServiceSoap12">

<textMessageEncoding maxReadPoolSize="64"

maxWritePoolSize="16"

messageVersion="Soap12"

writeEncoding="utf-8">

<readerQuotas maxDepth="32"

maxStringContentLength="8192"

maxArrayLength="16384"

maxBytesPerRead="4096"

maxNameTableCharCount="16384" />

</textMessageEncoding>

<httpTransport manualAddressing="false"

maxBufferPoolSize="524288"

maxReceivedMessageSize="65536"

allowCookies="false"

authenticationSchema="Anonymous"

bypassProxyOnLocal="false"

hostNameComparisonMode="StrongWildcard"

keepAliveEnabled="true"

maxBufferSize="65536"

proxyAuthenticationSchema="Anonymous"

realm=""

transferMode="Buffered"

unsafeConnectionNtlmAuthentication="false"

useDefaultWebProxy="true" />

</binding>

</customBinding>

</bindings>

<client>

<endpoint address="http://localhost/asmx/service.asmx"

binding="basicHttpBinding"

bindingConfiguration="StockServiceSoap"

contract="Client.WCFReference.StockServiceSoap"

name="StockServiceSoap" />

<endpoint address="http://localhost/asmx/service.asmx"

binding="customBinding"

bindingConfiguration="StockServiceSoap12"

contract="Client.WCFReference.StockServiceSoap"

name="StockServiceSoap12" />

</client>

</system.serviceModel>

</configuration>

 

本章小结

在本章中,我们主要介绍了WCF的基础知识,重点描述了服务的ABC属性。一个WCF服务由一组端点组成;每个端点都具有ABC属性,即:地址、绑定,以及契约。服务还包含能够描述其操作语义的行为特性,例如:线程和并发。有关服务行为特性的内容将在后面的章节中具体介绍。

服务可以寄宿在从“运行在Windows桌面上的控制台应用程序”到“数据中心中的IIS服务器”的任意操作系统进程内。本章中通过示例展示了各种寄宿方式。IIS寄宿是WCF服务最常用的寄宿方式。在一个安装了.NET 3.5运行环境的IIS服务器上,发送给SVC文件的请求会被自动分配给WCF处理。SVC文件中包含了指向服务具体实现的引用。服务的具体实现既可以位于SVC文件所在IIS虚拟目录下/bin目录中的某个Dll文件中,也可以位于服务器端全局程序集缓存(GAC)所加载的某个Dll文件中,还可以直接以源码方式内嵌在SVC文件中。

客户端与服务之间通过专用的消息进行通讯。为了提高开发效率,Visual Studio中提供了工具来创建客户端代理类来模拟服务器操作。客户端应用程序使用代理类与服务进行通讯。在代理类内部,WCFXML格式序列化服务参数,并向正确的服务端口地址发送XML格式的消息。客户端代理所需的配置信息保存在客户端的app.config配置文件中。代理类和配置文件由svcutil.exe工具或是Visual Studio中的“添加服务引用”功能生成。尽管工具能够极大的提高开发效率,但是有时,直接基于WCF API接口进行开发可能是更好的选择,这完全没问题。

ASMX服务遵循WS-I BP 1.1规范,而WCF内置的basicHttpBinding绑定也兼容该规范,因此WCF客户端可以使用basicHttpBinding绑定来访问ASMX服务。

根据本章所介绍的内容,应该能够完成WCF服务的定义、发布,以及使用。

 

posted on 2008-07-21 09:28  王嘉春  阅读(1799)  评论(4编辑  收藏  举报