Windows Communication Foundation(From MSDN)
Windows Communication Foundation 入门
作者 Rob Windsor,Visual Basic MVP、ObjectSharp Consulting 资深顾问
Windows Communication Foundation (WCF) 是 Microsoft 建立分布式系统的下一代平台。作为 .NET Framework 3.0 的一部分,它是设计用来巩固并扩展以前版本 Framework 的 API(即,ASP.NET Web Services、.NET Remoting, Enterprise Services (COM+) 和消息排队)。
WCF 入门并不难,但是在入门阶段有几个步骤可能有些难以理解。本文将向您介绍创建和使用两个简单服务的过程。为此,我们需要介绍服务类、服务主机、服务配置以及客户端代理。
要进行预排,需要安装 Visual Studio 2005、.NET Framework 3.0 和 .NET Framework 3.0(WCF 和 WPF)Visual Studio 2005 扩展程序。
要开始,请创建一个空白解决方案,称为 GettingStartedWithWCF。
服务类
服务类用于实现服务操作提供的功能。这只是一个在继承方面没有限制的常规 .NET 类。要使某个类成为服务类,我们可以在 System.ServiceModel 命名空间中应用 ServiceContract 和 OperationContract 属性。这些属性可修饰类声明本身或只修饰由该类实现的接口声明。使用这些属性并不意味着该类已经成为服务,仅意味着该类可能以服务形式公开。
我们将为服务类添加一个类库项目。为此,右键单击此解决方案,选择“添加”|“新项目”,然后创建名为 ServiceLib 的类库项目。
将文件 Class1.vb 重新命名为 MathService.vb。该类名称应该会自动更改为 MathService。否则,请手动更改。
添加对 System.ServiceModel 的引用。您会经常使用该引用,因为该程序集是 WCF 的核心。
为 MathService 类添加两个方法:一个方法是 Add,用于对两个整数求和;另一个方法是 Square,用于对一个双精度数求乘方。为 System.ServiceModel 命名空间添加 Imports 语句,然后使用 ServiceContract 属性修饰该类,并使用 OperationContract 属性修饰这两个方法。
Imports System.ServiceModel
<ServiceContract()> _
PublicClass MathService
<OperationContract()> _
PublicFunctionAdd(ByVal x AsInteger, ByVal y AsInteger) _
AsInteger
Return x + y
EndFunction
<OperationContract()> _
PublicFunction Square(ByVal x AsDouble) AsDouble
Return x * x
EndFunction
EndClass
如前所述,这些属性还可应用于接口。为了说明这一点,为 ServiceLib 项目添加一个名为 HelloService 的新类并添加以下所示的代码。
Imports System.ServiceModel
<ServiceContract()> _
PublicInterface IHello
<OperationContract()> _
Function SayHello(ByVal name AsString) AsString
EndInterface
PublicClass HelloService
Implements IHello
PublicFunction SayHello(ByVal name AsString) _
AsStringImplements IHello.SayHello
Return"Hello " + name
EndFunction
EndClass
服务主机
要使服务类中包含的功能可用于客户端应用程序,我们需要一个服务主机。它是一个应用程序,用于侦听来自客户端的请求,并创建用于处理那些请求的服务类实例。通过 WCF,您可以将 IIS 用作主机,也可以创建自己的自定义应用程序。
我们将创建两类主机,首先创建 Web 宿主。通过右键单击该解决方案,选择“添加”|“新网站”,然后选择“WCF 服务”模板,添加一个新网站。确保将位置设置为解决方案文件下的某个文件夹中的文件系统路径。
让我们简单了解一下 Service.vb 中的示例代码。该模板假定我们要在此处对服务的功能进行编码,因此,它拥有示例接口和具有适当属性(还有 DataContract,但本文不讨论此属性)的服务类。我们已经在 ServiceLib 项目中创建了服务类,因此可以删除 Service.vb 文件。
打开 Service.svc 并浏览一下各属性的值。Web 托管服务与先前的 ASP.NET 技术使用相同的模型。它们将您导航到的文件与包含代码的文件链接起来。这与 ASP.NET 中使用的代码隐藏技术相同。由于我们已经具有了已编译的服务类,因此可以删除此属性,然后输入 MathService 类型的完全限定的名称作为 Service 属性的值。
<% @ServiceHost Language=VB Service="ServiceLib.MathService" %>
最后,添加对 ServiceLib 项目的引用。
现在来构建解决方案。此时,解决方案资源管理器应如下所示。
这样做对 web 承载服务有益。现在,我们开始使用自定义应用程序作为宿主。为此,您可以使用 Windows 窗体应用程序、控制台应用程序或 Windows 服务。我们将使用控制台应用程序。
右键单击解决方案,选择“添加”|“新项目”。创建名为 ConsoleServiceHost 的控制台应用程序。
我们将需要引用 System.ServiceModel(用于将要做主机的类别)和 ServiceLib(用于服务类别),所以应首先添加那些引用值。
现在,拥有 .NET 应用程序主机服务十分简单。只需创建 ServiceHost 类型的实例,向其传递它将托管的服务类的 Type,然后调用 Open 方法。Open 方法使主机开始侦听请求。由于我们使用的是控制台应用程序,还将添加对 ReadLine 的调用,以使应用程序在宿主打开后持续运行。我们还将使用 Using 语句创建宿主,以确保该宿主在控制台应用程序关闭时能够正常关闭并释放。我们先前使用了 MathService,因此我们将在此项目中使用 HelloService。
Imports System.ServiceModel
Module Module1
Sub Main()
Dim t As Type = GetType(ServiceLib.HelloService)
Using host AsNew ServiceHost(t)
host.Open()
Console.WriteLine("Service started...")
Console.ReadLine()
End Using
EndSub
EndModule
服务配置
您可能已注意到,当我们建立宿主时,我们没有指明客户端应如何与服务进行通信。这可以通过服务配置实现。尽管该配置可以在代码中指定,但大多数情况下是在 .config 文件中使用 XML 实现的。
客户端要与服务进行通信,该客户端需要一个端点。一个端点由三个组件组成:地址、绑定和约定。它们通常被视为 WCF 的基础。地址是标识服务所在位置的 URI。绑定用于定义通信的规则(例如,编码、邮件是否需要保护、服务如何对客户端进行身份验证,等等)。最后,约定用于定义服务提供的操作。这基本上就是对服务类的引用。
WCF 附带一些为常见业务提供的内置绑定(参见下表)。可以通过配置来自定义这些绑定,也可以创建自己的自定义绑定以满足系统需要。为了简便起见,我们将使用遵循 SOAP 1.1 协议的基本 HTTP 绑定。
名称 |
传输 |
编码 |
互操作 |
basicHttpBinding |
HTTP/HTTPS |
文本 |
是 |
netTcpBinding |
TCP |
二进制 |
否 |
netPeerTcpBinding |
P2P |
二进制 |
否 |
netNamedPipeBinding |
IPC |
二进制 |
否 |
wsHttpBinding |
HTTP/HTTPS |
文本、MTOM |
是 |
wsFederationBinding |
HTTP/HTTPS |
文本、MTOM |
是 |
wsDualHttpBinding |
HTTP/HTTPS |
文本、MTOM |
是 |
netMsmqBinding |
MSMQ |
二进制 |
否 |
netIntegrationBinding |
MSMQ |
二进制 |
是 |
现在我们需要配置我们的主机。让我们从控制台应用程序开始。首先,我们需要一个位置来存放配置信息,因此,我们将添加一个 app.config 文件。右键单击该项目并选择“添加”|“新项目”,然后使用默认名称 (app.config) 创建一个应用程序配置文件。
要配置端点,我们需要地址、绑定和约定。我们将按相反的顺序了解这些内容。约定已经由我们指定的 ServiceHost 构造函数的类型确定。对于我们的应用程序,约定将为 ServiceLib.IHello。如前所述,绑定将为 basicHttpBinding。对于地址,我们将使用“http://localhost:8081/HelloService”,也就是说,客户端需要使用 HelloService 作为应用程序名称,通过 HTTP 将消息发送到我们本地服务器的端口 8081。
要使用下一部分介绍的代理创建向导,我们需要再执行一个步骤。与 ASP.NET Web 服务不同,WCF 服务不会自动显示服务描述(或元数据)。要启用此功能,我们需要添加一个服务行为并通知该服务使用新行为。
将 app.config 文件的全部内容替换为下面的 XML 文档来配置控制台主机。<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexEnabled">
<serviceMetadata
httpGetEnabled="true"
httpGetUrl="http://localhost:8081/HelloService" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="mexEnabled"
name="ServiceLib.HelloService">
<endpoint
address="http://localhost:8081/HelloService"
binding="basicHttpBinding"
contract="ServiceLib.IHello" />
</service>
</services>
</system.serviceModel>
</configuration>
配置 Web 主机与此大致相同。不同之处在于 XML 配置存在于 web.config 文件中,不需要我们指明端点的地址(该地址将由 Web 服务器上 Service.svc 文件的地址确定),而且 Web 主机公开的约定是 MathService,而不是 HelloService。
将 web.config 文件的全部内容替换为下面的 XML 文档来配置 Web 主机项目。<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexEnabled">
<serviceMetadata
httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="mexEnabled"
name="ServiceLib.MathService">
<endpoint
address=""
binding="basicHttpBinding"
contract="ServiceLib.MathService" />
</service>
</services>
</system.serviceModel>
</configuration>
客户端配置和代理
现在我们可以创建客户应用程序。右键单击解决方案,选择“添加”|“新项目”,然后创建一个名为 ServiceClient 的 Windows 应用程序。
为了能够使用我们的服务,我们需要客户端配置和代理。此配置与服务器端配置的工作原理非常类似,因为我们需要一个与服务主机兼容的端点。这些代理是客户端项目中用来代表服务的类,使我们能够以强类型化方式调用服务操作。
幸运的是,Visual Studio 的 .NET 3.0 扩展包括一个向导,该向导可以使用服务元数据为我们创建必需的配置和代理。您只需知道服务主机的正确 URL。
我们先来看看 web 承载服务。因为我们选择了基于文件的网站,所以我们需要知道 Visual Studio 正在使用的端口号。使用“属性”窗口可以找到该端口号。单击网站项目并查找端口号。在我的示例应用程序中,端口号为 13419。
通过添加 Web 项目名称 (WebServiceHost) 和 svc 文件名 (Service.svc),产生了一个 URL,即 http://localhost:13419/WebServiceHost/Service.svc。
要将客户端配置为使用此服务并创建代理类,请右键单击 ServiceClient 项目,然后选择“Add Service Reference”(添加服务引用)。为“Service URI”(服务 URI)输入服务的 URL,并为“Service reference name”(服务引用名)输入 MathProxy,然后单击“确定”。
现在,您应看到客户端应用程序在名为 Service References 的文件夹下有 app.config 文件和 MathProxy.map。在添加对控制台托管服务的引用后,我将深入讨论这些内容。
对于控制台主机服务,访问服务元数据所需要的 URL 存储在 app.config 文件中。如果查看 serviceMetadata 元素的 httpGetUrl 属性,您会发现已将该属性配置为 http://localhost:8081/HelloService。
我们需要启动主机,才能使用“添加服务引用”向导更新客户端。右键单击 ConsoleServiceHost 项目,然后选择“调试”|“启动新实例”。
一旦处于主机运行状态,右键单击 ServiceClient 项目,选择“Add Service Reference”(添加服务引用),然后在“Service URI”(服务 URI)字段中输入适当的 URL,并为“Service reference name”(服务引用名称)输入 HelloProxy。
添加引用后,务必停止控制台主机。您应发现 HelloProxy.map 已添加到客户端项目中的 Service References 文件夹下。
如果在 ServiceClient 项目中打开了 app.config 文件,你将看到向导为我们生成的 XML。它看起来很复杂,但实际上并非如此。为了更易于调整这些设置,向导在文件中保存了几个具有默认设置的属性。由于具有默认设置的属性不需要包含在该配置中,因此可以简化 XML 文档,如下所示。
此配置包含每一个主机的一种端点元素,而且地址和这些元素的绑定属性与主机中的值匹配。只是约定有所不同。这是因为客户端不引用原始服务类,而引用由向导根据服务元数据生成的客户端代理。<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:13419/WebServiceHost/Service.svc"
binding="basicHttpBinding"
contract="MathProxy.MathService"
name="BasicHttpBinding_MathService" />
<endpoint
address="http://localhost:8081/HelloService"
binding="basicHttpBinding"
contract="HelloProxy.IHello"
name="BasicHttpBinding_IHello" />
</client>
</system.serviceModel>
</configuration>
您在解决方案资源管理器中看不到代理类。这是因为它们被生成为不打算编辑的代码。要查看这些代理类,请单击 ServiceClient 解决方案,然后在解决方案资源管理器工具栏中单击“显示所有文件”按钮。完成此操作后,即可展开 HelloProxy.map 和 MathProxy.map 查看文件。
既然有适当的服务引用,我们可以构建用户界面测试服务。在 Form1 中添加三个 TextBox 和两个 Button,分别采用控件下面的红色名称。给定 TextBox 默认值,以便更容易对服务操作进行测试。
单击 SayNameButton 表示要从 HelloService 调用 SayHello 方法,将 NameTextBox 的内容作为参数传递。我们可以使用 MsgBox 显示结果。单击 AddButton 表示要从 MathService 调用 Add 方法,将 XTextBox 和 YTextBox 转换为 Integer 的内容作为参数传递。同样,可以使用 MsgBox 显示结果。
PublicClass Form1
PrivateSub SayNameButton_Click( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles SayNameButton.Click
Using ws AsNew HelloProxy.HelloClient
MsgBox(ws.SayHello(NameTextBox.Text))
End Using
EndSub
PrivateSub AddButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles AddButton.Click
Dim x AsInteger = Integer.Parse(XTextBox.Text)
Dim y AsInteger = Integer.Parse(YTextBox.Text)
Using ws AsNew MathProxy.MathServiceClient
MsgBox(ws.Add(x, y).ToString())
End Using
EndSub
EndClass
运行客户端时,还要确保控制台主机也在运行(它必须运行才能侦听对 HelloService 的请求)。为此,我们将使用多个启动项目。右键单击解决方案,然后选择“属性”。按如下所示配置启动属性,然后单击“确定”。
最后,我们准备好运行客户端和测试服务。按 F5 启动客户端和控制台主机,并单击这些按钮。如果您正确完成了上述所有操作,应看到以下结果(假定您使用的是上述屏幕快照中给出的值)。
总结
本文引导您完成了使用 WCF 创建和使用简单服务的过程,有时插入介绍了该技术的基础知识。现在,除了能构建服务之外,您还应该了解了服务类、服务主机、服务配置以及客户端代理。
后续文章将在此材料的基础上,进一步介绍绑定、安全性、数据序列化、实例、规范以及更多内容。
Rob Windsor 简介
Rob Windsor 是加拿大多伦多 ObjectSharp Consulting 的一位高级顾问和培训师。Rob 致力于使用前沿 Microsoft 技术的自定义业务应用程序的体系结构、设计和开发方面的研究。Rob 是多伦多 Visual Basic 用户协会 (TVBUG) 的主席。他也是 MSDN 加拿大 Speakers Bureau 的一名成员,经常在多伦多地区和整个加拿大的用户协会会议中发表演讲。鉴于其在开发人员社区中所做出的贡献,Rob 被公认为 Microsoft 最有价值专家 (MVP)。