和传统的分布式通信框架一样,WCF本质上提供一个跨进程、跨机器以致跨网络的服务调用。
在本例中,客户端和服务通过运行在相同的同一台机器上不同进程模拟,WCF的服务不能孤立地存在,需要寄宿于一个运行着的进程中,我们把承载WCF服务的进程称为宿主,为服务指定宿主的过程称为服务寄宿(Service Hosting)。在我们的计算服务应用中,采用了两种服务寄宿方式:通过自我寄宿(Self-Hosting)的方式创建一个控制台应用作为服务的宿主(寄宿进程为Hosting.exe);通过IIS寄宿方式将服务寄宿于IIS中(寄宿进程为IIS的工作进行W3wp.exe)。客户端通过另一个控制台应用模拟(进程为Client.exe)。接下来,我们就一步一步来构建这样的一个WCF应用。
一、构建解决方案
通过VS 2010创建一个空白的解决方案,添加如下四个项目。项目的类型、承载的功能和相互引用关系如下,整个项目在VS下的结构如图2所示。
- Contracts:一个类库项目,定义服务契约(Service Contract),引用System.ServiceMode程序集(WCF框架的绝大部分实现和API定义在该程序集中);
- Services:一个类库项目,提供对WCF服务的实现。定义在该项目中的所有WCF服务实现了定义在Contracts中相应的服务契约,所以Services具有对Contracts项目的引用;
- Hosting:一个控制台(Console)应用,实现对定义在Services项目中的服务的寄宿,该项目须要同时引用Contracts和Services两个项目和System.ServiceMode程序集;
- Client:一个控制台应用模拟服务的客户端,该项目引用System.ServiceMode程序集
二、创建服务契约
WCF包含四种类型的契约:服务契约、数据契约、消息契约和错误契约
服务契约:从功能上讲,服务契约抽象了服务提供的所有操作;而站在消息交换的角度来看,服务契约则定义了基于服务调用的消息交换过程中,请求消息和回复消息的结构,以及采用的消息交换模式。
我把服务契约简单理解成是放接口的地方,只不过在这个接口中要加上ServiceContract和OperationContract ServiceContract用于类或者结构上,用于指示WCF此类或者结构能够被远程调用,而OperationContract用于类中的方法(Method)上,用于指示WCF该方法可被远程调用。
在Contracts中:定义接口 ICalculator
using System.ServiceModel; namespace Artech.WcfServices.Contracts { [ServiceContract(Name = "CalculatorService", Namespace = "http://www.artech.com/")] public interface ICalculator { [OperationContract] double Add(double x, double y); [OperationContract] double Subtract(double x, double y); [OperationContract] double Multiply(double x, double y); [OperationContract] double Divide(double x, double y); } } 三、创建服务
当服务契约成功创建时,我们需要通过实现服务契约来创建具体的WCF服务。WCF服务CalculatorService定义在Services项目中,实现了服务契约接口ICalculator,实现了所有的服务操作。CalculatorService定义如下:
using Artech.WcfServices.Contracts; namespace Artech.WcfServices.Services { public class CalculatorService:ICalculator { public double Add(double x, double y) { return x + y; } public double Subtract(double x, double y) { return x - y; } public double Multiply(double x, double y) { return x * y; } public double Divide(double x, double y) { return x / y; } } } 四、通过自我寄宿方式寄宿服务
WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程。WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段。终结点由地址(Address)、绑定(Binding)和契约(Contract)三要素组成,由于三要素应为首字母分别为ABC,所以就有了易于记忆的公式:Endpoint = ABC。一个终结包含了实现通信所必需的所有信息,我们可以这样认识终结点的ABC:
- 地址(Address):地址决定了服务的位置,解决了服务寻址的问题
- 绑定(Binding):绑定实现了通信的所有细节,包括网络传输、消息编码,以及其他为实现某种功能(比如安全、可靠传输、事务等)对消息进行的相应处理。WCF中具有一系列的系统定义绑定,比如BasicHttpBinding、WsHttpBinding、NetTcpBinding等
- 契约(Contract):契约是对服务操作的抽象,也是对消息交换模式以及消息结构的定义
服务寄宿的目的就是开启一个进程,为WCF服务提供一个运行的环境。通过为服务添加一个或多个终结点,使之暴露给潜给的服务消费者。服务消费者最终通过相匹配的终结点对该服务进行调用。
添加终结点和定义服务行为的代码可以用下面的配置(在Hosting中添加一个配置文件App.config)
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="behavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9999/calculatorservice/metadata" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="behavior" name="Artech.WcfServices.Services.CalculatorService"> <endpoint address="http://127.0.0.1:9999/calculatorservice" binding="basicHttpBinding" bindingConfiguration="" contract="Artech.WcfServices.Contracts.ICalculator" /> </service> </services> </system.serviceModel> </configuration>
可以直接使用VS提供的配置工具。你可以通过VS的工具(Tools)菜单,选择“WCF Service Configuration Editor”子项,开启配置编辑器,新建一个配置,
在里面进行配置后保存为App.config ,然后放进Hosting项目中就一样了。
服务寄宿代码(Hositng中的Program.cs)
using System; using System.ServiceModel; using System.ServiceModel.Description; using Artech.WcfServices.Contracts; using Artech.WcfServices.Services; namespace Artech.WcfServices.Hosting { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(CalculatorService))) { //host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice"); //if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) //{ // ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); // behavior.HttpGetEnabled = true; // behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/calculatorservice/metadata"); // host.Description.Behaviors.Add(behavior); //} host.Opened += delegate { Console.WriteLine("CalculaorService已经启动,按任意键终止服务!"); }; host.Open(); Console.Read(); } } } }
注释部分的代码为:如果没有配置文件App.config时,通过编码的方式定义终结点和服务行为。
五、创建客户端,调用服务
服务被成功寄宿后(前提是将Hosting设为启动项,运行!),服务端便开始了服务调用请求的监听工作。
此外,服务寄宿将服务描述通过元数据的形式发布出来,相应的客户端就可以获取这些元数据创建客户端程序进行服务的消费。
在运行服务寄宿程序(Hosting.exe)的情况下,右键点击Client项目,在弹出的上下文菜单中选择“添加服务引用(Add Service References)”,如图6所示的添加服务引用的对话会显示出来。在地址栏上键入服务元数据发布的源地址:http://127.0.0.1:9999/calculatorservice/metadata,并指定一个命名空间,点击OK按钮,VS为为你生成一系列用于服务调用的代码和配置。
图6 添加服务引用
添加完引用后,会生成服务代理。
我们可以创建CalculatorServiceClient对象,执行相应方法调用服务操作。客户端进行服务调用的代码如下:
using System; using Client.CalculatorServices; namespace Artech.WcfServices.Client { class Program { static void Main(string[] args) { using (CalculatorServiceClient proxy = new CalculatorServiceClient()) { Console.WriteLine("x+y={2}, when x={0} and y={1}", 1, 2, proxy.Add(1, 2)); Console.WriteLine("x-y={2}, when x={0} and y={1}", 1, 2, proxy.Subtract(1, 2)); Console.WriteLine("x*y={2}, when x={0} and y={1}", 1, 2, proxy.Multiply(1, 2)); Console.WriteLine("x/y={2}, when x={0} and y={1}", 1, 2, proxy.Divide(1, 2)); Console.Read(); } } } }
运行后:
x + y = 3 when x = 1 and y = 2
x - y = -1 when x = 1 and y = 2
x * y = 2 when x = 1 and y = 2
x / y = 0.5 when x = 1 and y = 2