本文的WCF服务应用功能很简单,却涵盖了一个完整WCF应用的基本结构。希望本文能对那些准备开始学习WCF的初学者提供一些帮助。
在这个例子中,我们将实现一个简单的计算器和传统的分布式通信框架一样,WCF本质上提供一个跨进程、跨机器、跨网络的服务调用。在本例中,客户端和WCF应用服务通过运行在同一台机器上的不同进程模拟。
步骤一、构建整个解决方案
1.创建一个空白的解决方案
2.添加四个项目和引用及关系
Service.Interface 用于定义服务契约的类库项目,引用WCF核心程序集System.ServuceModel.dll
Service 用于定义服务类型的类库项目,由于服务类型需要实现定义在ervice.Interface中相应的接口,因此需要引用Service.Interface
Hosting 作为服务寄主的控制台应用,该项目同时引用Service.Interface、System.ServuceModel.dll、Service
Client 模拟服务的客服端控制台应用 该醒目引用System.ServuceModel.dll
步骤二、创建服务契约(一般将服务契约定义为接口)
WCF采用基于接口(MSDN上翻译为:服务协定)的交互方式实现了服务功能,以及客户端和服务端之间的松耦合。WCF包含五种类型的协定:服务协定、操作协定、消息协定、错误协定和数据协定。
从功能上讲,服务协定将多个相关的操作联系在一起,组成单个功能单元。协定可以定义服务级设置,如服务的命名空间、对应的回调协定以及其他此类设置,以及各种操作。
从消息交换的角度来讲,服务协定则定义了基于服务调用的消息交换过程中, 请求消息和回复消息的结构,以及采用的消息交换模式。
从使用编程语言的角度来讲,协定是通过所选的编程语言创建一个接口,然后将 ServiceContractAttribute 属性应用于该接口。通过实现该接口,可生成实际的服务代码。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.ServiceModel; 7 8 namespace Service.Interface 9 { 10 /// <summary> 11 /// 计算器 12 /// </summary> 13 [ServiceContract(Name = "CalculatorService",Namespace="http://www.artech.com/")] 14 public interface ICalculator 15 { 16 /// <summary> 17 /// 加 18 /// </summary> 19 /// <param name="x"></param> 20 /// <param name="y"></param> 21 /// <returns></returns> 22 [OperationContract] 23 double Add(double x,double y); 24 25 /// <summary> 26 /// 减 27 /// </summary> 28 /// <param name="x"></param> 29 /// <param name="y"></param> 30 /// <returns></returns> 31 [OperationContract] 32 double Subtract(double x,double y); 33 34 /// <summary> 35 /// 乘 36 /// </summary> 37 /// <param name="x"></param> 38 /// <param name="y"></param> 39 /// <returns></returns> 40 [OperationContract] 41 double Multiply(double x,double y); 42 43 /// <summary> 44 /// 除 45 /// </summary> 46 /// <param name="x"></param> 47 /// <param name="y"></param> 48 /// <returns></returns> 49 [OperationContract] 50 double Divide(double x,double y); 51 } 52 }
步骤三、实现WCF服务契约
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Service.Interface; 7 8 namespace Service 9 { 10 public class CalculatorService:ICalculator 11 { 12 13 14 public double Add(double x, double y) 15 { 16 return x + y; 17 } 18 19 public double Subtract(double x, double y) 20 { 21 return x - y; 22 } 23 24 public double Multiply(double x, double y) 25 { 26 return x * y; 27 } 28 29 public double Divide(double x, double y) 30 { 31 return x / y; 32 } 33 } 34 }
步骤四、通过自我寄宿的方式寄宿服务
WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程。WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段。
终结点主要由地址(Address)、绑定(Binding)和协定(Contract)三要素组成,如图所示。由于三要素应为首字母分别为ABC,所以就有了易于记忆的公式:Endpoint = ABC。一个终结包含了实现通信所必需的所有信息。如下图。
终结点三要素
- 地址(Address):一个指示可以查找终结点的位置的地址。地址决定了服务的位置,解决了服务寻址的问题
- 绑定(Binding):一个指定客户端如何与终结点进行通信的绑定。绑定实现了通信的所有细节,包括网络传输、消息编码,以及其他为实现某种功能(比如安全、可靠传输、事务等)对消息进行的相应处理。WCF中具有一系列的系统定义绑定,比如BasicHttpBinding、WsHttpBinding、NetTcpBinding等,
- 协定(Contract):一个标识可用操作的协定。协定是对服务操作的抽象,也是对消息交换模式以及消息结构的定义。
- 行为(Behavior):一组指定终结点的本地实现细节的行为。
服务寄宿的目的就是开启一个进程,为WCF服务应用提供一个运行的环境。通过为服务添加一个或多个终结点,使之暴露给潜在的服务调用者。服务调用者最终通过相匹配的终结点对该服务进行调用。
一)代码方式实现寄宿
1 using Service; 2 using Service.Interface; 3 using System.ServiceModel; 4 using System.ServiceModel.Description; 5 6 namespace Hosting 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 //在进行真正的开放时一般在配置文件里面进行配置添加终结点和服务行为的定义 13 using(ServiceHost host = new ServiceHost(typeof(CalculatorService))) 14 { 15 //指定绑定和总结点的地址 16 host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:3721/calculatorservice"); 17 //数据的发布 18 if(host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) 19 { 20 //创建服务行为 21 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); 22 //是否发布元数据以便使用HTTPS/GET请求进行检索 23 behavior.HttpGetEnabled = true; 24 //使用HTTPS/GET请求的元数据发布的位置 25 behavior.HttpGetUrl = new Uri("http://127.0.0.1:3721/calculatorservice/metadata"); 26 //添加到发布上 27 host.Description.Behaviors.Add(behavior); 28 } 29 host.Opened += delegate 30 { 31 Console.WriteLine("CalculatorService已经启动,按任意键终止服务!"); 32 }; 33 //通讯状态转换到已打开 34 host.Open(); 35 Console.ReadLine(); 36 } 37 } 38 } 39 }
1) WCF服务寄宿通过一个特殊的对象完成:ServiceHost。在上面的代码基本实现的功能说明,基于WCF服务应用的类型(typeof(CalculatorService))创建了ServieHost对象,并添加了一个终结点。具体的地址为http://127.0.0.1:3721/calculatorservice/metadata,采用了WSHttpBinding,并指定了服务协定的类型ICalculator。
2) 松耦合是SOA的一个基本的特征,WCF服务应用中客户端和服务端的松耦合体现在客户端只需要了解WCF服务基本的描述,而无需知道具体的实现细节,就可以实现正常的WCF服务调用。WCF服务的描述通过元数据(Metadata)的形式发布出来。WCF中元数据的发布通过一个特殊的服务行为ServiceMetadataBehavior实现。在上面提供的服务寄宿代码中,我们为创建的ServiceHost添加了ServiceMetadataBehavior,并采用了基于HTTP-GET的元数据获取方式,元数据的发布地址通过ServiceMetadataBehavior的HttpGetUrl指定。在调用ServiceHost的Open方法对服务成功寄宿后,我们可以通过该地址获取服务相关的元数据。
3) 运行已经生成的hosting.exe,
4)然后在浏览器地址栏上键入http://127.0.0.1:3721/calculatorservice/metadata,你将会得到以WSDL形式体现的服务元数据,如下图所示。
二)配置文件方式实现寄宿
在实际应用中,对于WCF应用服务的寄宿,一般不会直接通过编码的方式进行终结点的添加和服务行为的定义,而是通过写配置文件的方式实现,这样可以方便修改。
1) 现在我在Hosting项目中添加一个app.config配置文件,把下面的配置信息添加到配置文件app.config中。
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <startup> 4 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> 5 </startup> 6 <system.serviceModel> 7 8 <behaviors> 9 <serviceBehaviors> 10 <behavior name="metadataBehavior"> 11 <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:3721/calculatorservice/metadata" /> 12 </behavior> 13 </serviceBehaviors> 14 </behaviors> 15 16 <services> 17 <service behaviorConfiguration="metadataBehavior" name="Service.CalculatorService"> 18 <endpoint address="http://127.0.0.1:3721/calculatorservice" 19 binding="wsHttpBinding" bindingConfiguration="" contract="Service.Interface.ICalculator" /> 20 </service> 21 </services> 22 23 </system.serviceModel> 24 </configuration>
2) 增加app.config配置文件与配置信息之后,我们原来写的寄宿代码就不能使用了,需要进行服务寄宿代码的修改,而且代码会变的更简洁,只需几行代码就可以了。代码如下。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Service; 7 using Service.Interface; 8 using System.ServiceModel; 9 using System.ServiceModel.Description; 10 11 namespace Hosting 12 { 13 class Program 14 { 15 static void Main(string[] args) 16 { 17 using(ServiceHost host = new ServiceHost(typeof(CalculatorService))) 18 { 19 ////指定绑定和终结点的地址 20 //host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:3721/calculatorService"); 21 ////数据的发布 22 //if(host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) 23 //{ 24 // //创建服务行为 25 // ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); 26 // //是否发布元数据以便使用HTTPS/GET请求进行检索 27 // behavior.HttpGetEnabled = true; 28 // //使用HTTPS/GET请求的元数据发布的位置 29 // behavior.HttpGetUrl = new Uri("http://127.0.0.1:3721/calculatorService/metadata"); 30 // //添加到发布上 31 // host.Description.Behaviors.Add(behavior); 32 //} 33 host.Opened += delegate 34 { 35 Console.WriteLine("CalculatorService已经启动,按任意键终止服务!"); 36 }; 37 //通讯状态转换到已打开 38 host.Open(); 39 Console.ReadLine(); 40 } 41 } 42 } 43 }
3) 执行hosting.exe应用程序,结果如下图。
下一章节讲怎么通过客户端去调用我们成功寄宿后的服务 (欢迎关注)