WCF: 从初学者的角度来看 & 教程






4.81/5 (99投票s)
本文试图深入探讨 WCF 是什么,如何编写 WCF 服务
引言
嗯,一个月前我写了一篇关于 ASP.NET Web 服务的文章,可以在这里找到 https://codeproject.org.cn/Articles/524983/Web-Services-From-a-beginners-perspective,我收到一条评论说 .ASMX Web 服务现在很少用了,这在一定程度上是正确的。因此,我决定写一篇文章来介绍 WCF,如何编写 WCF 服务以及相关内容。
我们还是尽快开始文章吧。
嗯,WCF 代表 **Windows Communication Foundation**,它最初随 **.NET 3.0** 框架与 **WPF**、**Windows CardSpace** 和 **WorkFlow Foundation** 一起发布。
从初学者的角度来看,WCF 到底是什么意思?让我们将缩写分解成单个实体并尝试调试。它包含三个词:a) Windows b) Communication c) Foundation。
初学者可能会看一眼这些词,然后想,嗯,它涉及到 Windows 平台,用于通信和连接应用程序。嗯,他/她第一次猜对了一半……
可以总结一个想到的定义:WCF 是一种在 Windows 平台上开发和部署服务所用的技术。不同的书籍对 WCF 有不同的描述,但核心思想几乎相同,都指向在 Windows 平台上开发和部署服务。
MSDN 说,WCF 是一个构建面向服务应用程序的框架。所以 WCF basically 围绕着开发高度依赖服务的应用程序。
WCF 的必要性
接下来可能萦绕在脑海中的问题是为什么需要 WCF,它有什么用?是的,在有了初步了解后,这确实是最可能提出的问题。在 WCF 之前,有一些技术可以完成客户端和服务器之间的通信任务,它们各有优缺点。例如
- ** .ASMX Web 服务** 允许从任何平台访问内容。
- ** MSMQ** 允许消息排队,这样即使服务器断开连接,客户端和服务器之间的通信也是可能的。
- ** .NET Remoting** 服务允许在 Windows 操作系统上客户端和服务器之间传输数据。
因此,如果开发人员需要开发具有上述所有功能的架构,他需要学习上述技术,这是一项艰巨的任务。因此,Microsoft 引入了 WCF 来克服这些缺点。基本上,WCF 将上述技术统一到一个编程模型中,以便于开发面向服务的架构。
我希望读者现在已经对 WCF 有了初步的了解,我们继续。
WCF 架构
下面是 WCF 架构图(来源:Google Images)
WCF 具有多层架构,即 **Contracts 层**、**Service Runtime 层**、**Messaging 层** 和 **Activation and Hosting 层**。我将尝试用简单的语言解释主要的架构内容。
Contracts 层
通俗地说,合同可以看作是两个或多个方之间就某些商品或服务达成的协议。合同将定义需要提供哪些服务,如何提供,有关地点等的详细信息。WCF Contracts 层也是如此。**它是两台机器之间关于要交换的消息的条款和条件的协议。** 让我们看看 Contracts 层中的各个项目。
- **数据合同 (Data Contract):** 服务和客户端之间关于要交换数据的协议。它还定义了服务为了与客户端交互而应使用的数据结构和参数。
- **消息合同 (Message Contract):** 允许控制 WCF 生成和使用的 SOAP 消息。它还提供对消息头和消息体的直接访问,以便根据需要修改 SOAP 消息。在正常情况下,开发人员通常不太关注消息合同,但当需要互操作性或需要在消息级别维护安全性时,就会出现消息合同及其使用的需求。
- **服务合同 (Service Contract):** 定义服务支持的操作类型。它还向客户端公开某些信息,例如
- 消息中的数据类型。
- 操作的位置,或方法的定义位置
- 协议信息和序列化格式
- 消息交换模式(消息的行为是一次性、双工还是请求/响应类型)
- **策略和绑定 (Policy and Binding):** 指定重要信息,如安全性和协议。
让我们切换到 Service Runtime 层
**Service Runtime 层** 指定和管理服务在服务运行期间发生的各种行为。服务行为,或者可以亲切地称为 **Service Behaviors**,基本上是修改和控制 WCF 运行时特性的对象。以下是 Service Runtime 层管理的 Service Behaviors 列表
- **节流行为 (Throttling Behavior):** 确定处理的消息数量,该数量根据服务需求而变化。
- **错误行为 (Error Behavior):** 指定在运行时任何消息出错时应采取的行动类型。
- **元数据行为 (Metadata Behavior):** 指定元数据是否跨网络可用。
- **实例行为 (Instance Behavior):** 指定可用于处理消息的服务实例数量。
- **消息检查 (Message Inspection):** 在运行时完全或部分检查消息。
- **事务行为 (Transaction Behavior):** 启用事务操作,如果在运行时任何过程失败,事务将回滚。
- **调度行为 (Dispatch Behavior):** 确定消息的处理和分派。
- **并发行为 (Concurrency Behavior):** 指定消息是顺序处理还是并发处理。
- **参数筛选 (Parameter Filtering):** 筛选消息头并根据消息头的筛选器执行预设操作。
让我们讨论一下 **Messaging 层** 的内容:
Messaging 层负责交换模式及其格式。它由以下组件和通道组成
- **WS-Security 通道 (WS-Security Channel):** 通过实现 WS-Security 规范来启用消息安全性。您可以在此处找到 WS Security 的广泛介绍:http://msdn.microsoft.com/en-us/library/ms977327.aspx
- **WS 可靠消息通道 (WS Reliable Messaging Channel):** 保证消息的送达。
- **编码器 (Encoders):** 为消息提供多种编码器,如 Binary、XML、Text、MTOM。
- **HTTP 通道 (HTTP Channel):** 表明使用 HTTP 来传输消息。
- **TCP 通道 (TCP Channel):** 表明使用 TCP 来传输消息。
- **事务流通道 (Transaction Flow Channel):** 管理事务性消息模式。
- **命名管道通道 (Named Pipe Channel):** 启用进程间通信。
- **MSMQ 通道 (MSMQ Channel):** 使服务能够与 MSMQ 配合使用。
让我们继续讨论 **Hosting and Application 层**
这一层提供了多种服务激活和托管的选项。托管可以有两种类型:自托管,或托管在外部代理上,例如 IIS。此层提供的各种选项如下:
- **Windows 激活服务 (Windows Activation Services):** 当 WCF 应用程序部署在运行 WAS 的系统上时,可以自动激活它们。
- **.EXE:** WCF 服务也可以作为可执行文件运行。
- **Windows 服务 (Windows Services):** WCF 服务也可以作为 Windows 服务运行。
- **COM+:** WCF 服务也可以作为 COM+ 应用程序运行。
在讨论完架构后,让我们继续讨论 WCF 的功能,这也是一个重要的领域。
WCF 的功能
WCF 的功能可以描述如下:
- 终结点支持
- 改进的传输层
- 排队支持
- 改进的事务处理
- 改进的安全性
- 在各种环境中提供托管支持
- AJAX 集成和 JSON 支持
让我们从简要讨论每个方面开始
**终结点支持 (Endpoint Support):** 终结点基本上是网络中可以发送消息的资源或实体。终结点负责客户端和服务之间的通信。终结点的元素包括
- 用于定位服务的地址
- 用于与服务通信的绑定
- 用于指定与服务通信时要执行功能的合同。
- 用于指定服务终结点运行时行为的终结点行为。
这些地址、绑定和合同通常被称为 WCF 的 ABC。
让我们来讨论一下这些 ABC。**地址 (Addresses):** 要将消息发送到服务,您需要知道它的地址。WCF 中的地址将包含以下项目
- 方案 (Scheme): 基本上是使用的传输协议。
- 计算机 (Machine): 计算机的完全域名。
- 端口 (Port): 定义服务运行的端口号。
- 路径 (Path): 服务的路径。
例如可以写成:
- scheme://domain_name:port/path
- https://:8080/services/MyFirstService
在上面的例子中,一个服务在本地托管,HTTP 是传输协议,8080 是使用的端口,路径是 services/MyFirstService。
**绑定 (Bindings):** 促进客户端和服务之间的通信。指定用于访问服务的通信协议。以下是预定义绑定元素的列表:basicHttpBinding
wsHttpBinding
wsDualHttpBinding
wsFederationHttpBinding
netTcpBinding
netNamedPipeBinding
netMsmqBinding
msmqIntegrationBinding
netPeerTcpBinding
嗯,我将简要讨论其中一个绑定,其他绑定的使用和实现细节可以在 MSDN 和 CP 上轻松搜索到……
basicHttpBinding
: 这种类型的绑定由 WCF 服务用于配置和公开能够与 .ASMX Web 服务和客户端通信的终结点。使用的传输协议是 HTTP,而使用的消息编码器是 Text/XML。默认情况下,此绑定的安全性是禁用的,要启用安全性,需要配置 BasicHttpSecurityMode
。看看下面的代码,了解如何配置 basicHttpBinding
的宿主。<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyFirstService">
<host>
<baseAddresses>
<add baseAddress="https://:8080/MyFirstservice"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="MyFirstService">
</endpoint>
</service>
</services>
</system.serviceModel>
</configuration>
让我们来讨论 WCF ABC 中的 C(合同)
**合同 (Contracts):** 客户端和服务之间的协议。3 个基本合同是:
- **服务合同 (Service Contracts):** 定义服务的各种方法。
- **数据合同 (Data Contract):** 定义服务方法使用的数据类型。
- **消息合同 (Message Contract):** 在创建消息时控制消息头的能力。
**WCF 的第二个功能是改进的传输层:** 与 .ASMX Web 服务不同,WCF 服务使用多种协议在客户端和服务之间进行通信。它使用 HTTP、TCP、HTTPS、命名管道。因此,传输功能得到了改进。
**排队支持 (Queuing Support):** WCF 支持排队,允许将消息存储在队列中,然后及时发送,从而提供一致的通信状态。
**改进的事务支持 (Improved Transactional Support):** WCF 提供了改进的事务支持,这意味着如果在服务运行时任何过程失败,事务将不会提交,而是回滚。
**改进的安全性 (Improved Security):** 身份验证在 WCF 中起着重要作用,WCF 还确保在传输过程中消息的顺序不会被篡改。
**在各种环境中提供托管支持 (Hosting support on various environments):** WCF 允许在多种环境中托管服务,例如 Windows NT 服务、Windows 窗体、控制台应用程序、IIS、Windows 激活服务 (WAS)。IIS 提供了自动启动和停止服务的额外优势。
**AJAX 集成和 JSON 支持 (AJAX Integration and JSON Support):** WCF 支持 ASP.NET AJAX 和 JSON 数据格式,这允许 WCF 服务向 AJAX 客户端公开操作。JSON 是一种用于在启用了 AJAX 的服务与其客户端之间进行数据交换的数据格式。现在让我们开始编写代码部分。
背景
没有特别的先决条件,只需要安装 Visual Studio 2008 客户端即可。
Using the Code
我们将首先编写一个简单的 WCF 服务,并通过内置的 WCF 测试客户端进行测试。
- 打开 Visual Studio 2008,然后单击 **文件 > 新建 > 项目**。
- 首先创建一个**空白解决方案**。我们将在下一步添加项目。
将解决方案命名为 MyFirstServiceSolution,并选择要存储代码的位置。 - VS 会创建一个解决方案,然后右键单击解决方案资源管理器 **添加 > 新项目**。
- 单击“添加 > 新项目”后,将打开一个包含几个选项的对话框。选择**WCF 服务库**,将名称命名为 MyServiceLibrary,选择所需的位置,然后单击“确定”。
- 将一个新的 WCF 服务库添加到解决方案中,其中包含 2 个默认类文件:*Service1.cs* 和 *IService1.cs*,提供 WCF 服务库的默认实现。由于我们将自行配置库,因此请删除这两个类文件。
- 现在,右键单击 WCF 服务库并添加一个新类文件,将其命名为 *MyService.cs*。
- 打开 *MyService.cs*,最初它只有一个名为
MyService
的类。我们需要定义服务需要公开什么样的数据。原始数据类型,如 int、string、float,不需要作为数据合同公开,.NET 引擎基本上都能理解;而用户定义的数据类型,如类、枚举,需要作为数据合同公开。因此,定义一个名为 Maths 的类并在其中定义两个属性。Data
合同类的各个成员需要作为数据成员公开。需要添加System.Runtime.Serialization
命名空间才能使数据合同生效。然后,我们将不得不定义我们的服务合同,它确保服务公开哪些操作。添加一个接口,称之为IMaths
,并在其中添加单独的方法,这些方法将接受 Data 合同类作为参数进行处理。通过包含System.ServiceModel
命名空间,服务合同将生效。接下来是服务的实际实现,让MyService
类继承接口及其操作。根据需要定义操作的功能。在下面的代码中,在MyService
类中定义了常规的算术运算。构建解决方案,应该可以毫无错误地编译。
using System;
<pre>using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ServiceModel;
namespace MyServiceLibrary
{
[DataContract]
public class Maths
{
[DataMember]
public int Number1 { get; set; }
[DataMember]
public int Number2 { get; set; }
}
[ServiceContract]
public interface IMaths
{
[OperationContract]
int Addition(Maths obj1);
[OperationContract]
int Subtraction(Maths obj2);
[OperationContract]
int Multiplication(Maths obj3);
[OperationContract]
int Division(Maths obj4);
}
class MyService:IMaths
{
#region IMaths Members
public int Addition(Maths obj1)
{
return (obj1.Number1 + obj1.Number2);
}
public int Subtraction(Maths obj2)
{
return (obj2.Number1 - obj2.Number2);
}
public int Multiplication(Maths obj3)
{
return (obj3.Number1 * obj3.Number2);
}
public int Division(Maths obj4)
{
return (obj4.Number1 / obj4.Number2);
}
#endregion
}
}
- 接下来,我们需要配置 *App.Config* 文件。首先,我们需要**更改服务名称**以指向 MyServiceLibrary.MyService。最初它将指向
MyServiceLibrary.Service1
,但由于我们删除了 *Service1.cs* 并包含了 MyService.cs,因此我们需要在配置文件中修改服务节点。 - 接下来我们需要配置终结点。**更改合同名称**以指向
MyServiceLibrary.IMaths
。在这里,我配置了两个不同的终结点,使服务可以通过两种方式进行通信。**使用了 wsHttpBinding 和 basicHttpBinding**。已将基地址更改为更简单的路径。 - 嗯,当您尝试调试 WCF 服务并进行所有更改后,可能会出现“访问被拒绝”异常,提示无法访问基址。以管理员模式调试您的 WCF 服务,然后它就会起作用。
- 现在让我们调试服务并看看它的工作原理。按 F5,然后将启动一个 WCF 测试客户端,如下所示。.NET 引擎会查找基址并 ping 它以获取服务的元数据,最终向我们显示服务公开的所有操作。
- 它显示了两次相同的操作,因为我们添加了两种不同的绑定来使用。双击任何操作,然后您可以提供参数值并调用服务。服务将相应地返回响应。例如,让我们调用“加法”操作,如下所示:
- 输入一些随机值并单击“调用”按钮,服务将根据操作的功能返回响应,如下所示:
在上面的例子中,我们从头开始创建了一个 WCF 服务库,它涉及到只创建一个类并在此类中实现所有内容,尽管实际上不必将所有内容都实现在同一个类中。这只是为了演示。
接下来我们将看到如何从客户端调用和使用 WCF 服务。让我们开始吧。
- 再次打开您的 Visual Studio,然后单击 **新建 > 网站**,如下所示。
- 将打开一个对话框,**选择 WCF 服务**,然后指定要存储它的位置。
- 该模板将向解决方案添加一个 WCF 服务项目,其中包含两个类文件 IService.cs、Service.cs 和一个 Service.svc 文件。
- 当我们要将服务托管在 IIS 中时,Service.svc 文件很有用。*Service.cs* 和 *IService.cs* 类将提供一些默认的服务实现。暂时将它们保持原样。构建解决方案,将 *Service.svc* 文件设置为启动页并调试。您将看到 ASP.NET 开发服务器会浏览到一个 URL 并返回类似如下的服务实现。这意味着服务已创建并正在运行。
- 点击页面中的 URL,您将被重定向到 **Web Services Description Language** (WSDL) 页面。
- 让我们查看 Service.svc 文件的结构。
<%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>
上面代码片段中粗体显示的 Service 元素指的是 *theService.cs* 文件中的
Service
类,该类进一步存储在 App_Code 文件夹下。以上只是对 WCF 服务及其功能的初步介绍,让我们开始编写一个 WCF 服务并尝试在控制台应用程序中调用它。
- 将 *IService.cs*、*Service.cs* 文件重命名为更合适的名字,例如 *IMathService.cs* 和 *MathService.cs*。相应地,您需要修改 *Service.svc* 和 *web.config* 文件中的名称。看看下面的 *IMathService.cs* 代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
// NOTE: If you change the interface name "IService" here, you must also update the reference to "IService" in Web.config.
[ServiceContract]
public interface IMathService
{
[OperationContract]
int Addition(Math obj1);
[OperationContract]
int Subtraction(Math obj2);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class Math
{
int number1, number2;
[DataMember]
public int Number1
{
get { return number1; }
set { number1 = value; }
}
[DataMember]
public int Number2
{
get { return number2; }
set { number2 = value; }
}
}
在这里,代码中有一个 Math 类,它是一个 DataContract,因为它充当用户定义的数据类型。它有两个成员和两个属性来定义数字。然后我们有一个名为 IMathInterface
的 ServiceContract
,它向客户端公开两个操作。
- 看看 *MathService.cs* 文件:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
// NOTE: If you change the class name "Service" here, you must also update the reference to "Service" in Web.config and in the associated .svc file.
public class MathService : IMathService
{
#region IMathService Members
public int Addition(Math obj1)
{
return (obj1.Number1 + obj1.Number2);
}
public int Subtraction(Math obj2)
{
return (obj2.Number1 - obj2.Number2);
}
#endregion
}
MathService
类继承了 IMathService
接口,并提供了对服务公开的操作的实现。
- **构建解决方案,将 Service.svc 设置为启动页并调试**。它应该可以毫无错误地工作,您将在浏览器中看到如下所示的服务页面:
它显示了如何在客户端应用程序中使用这个 WCF 服务。现在让我们尝试编写一个控制台应用程序来调用这个服务。敬请关注……
- 右键单击解决方案,然后单击 **添加 > 新项目**,如下所示:
- 将打开一个对话框,从模板列表中选择**控制台应用程序**,**将此应用程序存储在上述服务所在的同一解决方案中**,并为应用程序命名为 DemoClient。
- 将添加一个控制台应用程序项目到解决方案中,现在我们需要在这个应用程序中引用 WCF 服务。*右键单击引用 > 添加服务引用*。
- 这将打开**添加服务引用**窗口。我们需要搜索要添加的 WCF 服务,在地址文本框中输入其 URL,然后按“转到”。服务将显现,其服务和操作将列出。
将命名空间命名为 localhost,因为它是域名(提供域名是约定俗成),然后按“确定”。服务引用将被添加到控制台应用程序项目中。
现在打开控制台应用程序的 program.cs 文件,开始编写代码,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DemoClient
{
class Program
{
static void Main(string[] args)
{
localhost.MathServiceClient obj = new DemoClient.localhost.MathServiceClient();
localhost.Math mathobj = new DemoClient.localhost.Math();
mathobj.Number1 = 10;
mathobj.Number2 = 5;
Console.WriteLine("Addition continues..");
Console.WriteLine(obj.Addition(mathobj));
Console.WriteLine("Subtraction continues..");
Console.WriteLine(obj.Subtraction(mathobj));
Console.ReadLine();
}
}
}
我将解释上面的代码。我们没有做任何特别的事情,我们只是创建了一个 MathService 客户端对象obj
,并尝试访问 WCF 服务中公开的操作。由于 WCF 服务操作接受 Math 类的参数,因此我们必须创建一个 Math 类的对象,如上所示的 mathobj
。这个 mathobj
将能够访问 Math
类的属性。然后我们为属性赋了一些值,并直接调用了 WCF 服务公开的各个操作 Addition()
和 Subtraction()。
- 现在,我们需要调试我们的控制台应用程序,看看实现是否成功。嗯,**右键单击解决方案中的控制台应用程序名称,并将其设置为“启动项目”**。
- 现在调试应用程序,如果一切正常,控制台应用程序将毫无错误地工作,如下所示:
因此,我们已经非常成功地创建了一个 WCF 服务并通过客户端(在本例中为控制台应用程序)对其进行了调用。**我想强调一下,当向应用程序添加服务引用时会发生什么,其内部机制和工作原理**。让我们看看。
- 点击服务引用下的 localhost 服务,然后点击**“显示所有文件”**。
- **将显示文件列表**,如下所示:
- 点击 Reference.cs 下的 Reference.svcmap 分类,其中将包含映射、序列化和代理类等内容。例如:在下图中,我们可以看到一个名为 Math 的部分类已被创建,其中 DataMembers 已被序列化,这意味着用户定义的数据类型 Math 类在 WCF 服务被客户端调用时会被序列化,然后被引用。
- 再看下图,操作的实现已经创建,这意味着如果我们要在客户端应用程序中调用这些操作,我们也需要以相同的方式进行编码。此外,我们还可以看到一个名为
MathServiceClient
的代理类已被创建,我们只需要在主方法中实例化该代理类,然后就可以使用 WCF 服务公开的操作了。
要点是,当我们向客户端应用程序添加服务引用时,Visual Studio 会查看服务的 WSDL 文件,下载它,并在 Reference.cs 文件中准备类似的方法和函数。之后,我们只需要在我们的主应用程序中引用这些方法和函数。
我想我现在已经到了文章的结尾。我已经讨论了:- WCF 是什么,它的架构、功能、用途。
- 如何从头开始创建 WCF 服务库并通过 WCF 测试客户端进行测试。
- 如何创建 WCF 服务并在客户端应用程序中调用它。
在文章的下一部分,我将讨论如何在 ASP.NET Web 应用程序中调用 WCF 服务以及如何使用 ASP.NET AJAX 编写 WCF 服务(这篇文章很快就会发布)……
兴趣点
嗯,在写这篇文章的过程中我学到了很多。
我希望与读者分享的一个技巧是,当您声明 DataContract
或 ServiceContract
时,如果这些术语没有高亮显示(显示错误),可能是因为我们没有包含所需的命名空间。您可以按 Ctrl+点键来实施命名空间,而不是手动输入。看看下面的内容:
当您尝试重构接口时,也同样适用。
添加了一个 zip 文件夹,其中包含本文引用的源代码。它基本上有两个文件夹:MyFirstServiceSolution
(从头开始编写的 WCF 服务库)和 WCF_WebService
(包含一个 WCF 服务和一个控制台应用程序)。请好好利用它。
我希望将部分功劳归于 **MSDN 和 C# 和 ASP.NET 的黑皮书**,我从中参考了某些定义和内容。
本文的续篇可以在这里找到:https://codeproject.org.cn/Articles/570036/WCF-From-a-Beginners-perspective-and-a-tutorial-Se。它帮助读者在 ASP.NET 网站中调用 WCF 服务,并创建支持 AJAX 的 WCF 服务。请查看并享受编写 WCF 服务的乐趣。
历史
WCF 文章 1.0 版本完成于 2013 年 3 月 25 日。