跟我一起学WCF(4)——第一个WCF程序

一、引言

  前面几篇文章分享了.NET 平台下其他几种分布式技术,然而前面几种分布式技术专注于某一特定的领域,并且具有不同编程接口,这使得开发人员需要掌握多个API的使用。基于这样的原因,微软在.NET 3.0时实现了WCF。WCF是.NET平台下各种分布式技术的集成,它将前面介绍的几种分布式技术完全整合在一起,并提供了一套统一的编程接口(API)。对于,开发人员来来说只需要掌握WCF一套的API,就可以实现之前分布式技术所实现的所有功能。

二、WCF详细介绍

   WCF(Windows Communication Foundation)是微软为构建面向服务的应用程序(SOA)而提供的统一编程模型,借助该模型,使得在构建分布式系统中,无需再考虑如何去实现通信的相关的问题,使开发人员更加关注与系统业务逻辑本身的实现。而在WCF中,各个Application之间的通信是由Endpoint(终结点)来实现的。下面详细介绍下WCF几个重要的概念。

2.1 EndPoint 详细介绍

   服务的提供者将服务通过一个或多个终结点发布给潜在的服务消费者,服务的消费者则通过与之匹配的终结点对服务进行消费。终结点由地址(Address)、绑定(Binding)和契约(Contract)三要素组成。如下图所示。由于它们的首字母分别是A、B、C。所以就有了:EndPoint = ABC。

  这三个要素在WCF通信中起到的作用分别是:

  • 地址(Address):地址标识了服务的位置,提供寻址的辅助信息和标识了服务的真实身份。Address解决了Where the WCF service?的问题。
  • 绑定(Binding):绑定实现了通信的所有细节,包括网络传输,消息编码,以及其他为实现某种功能对消息进行的相应处理,例如安全、可靠传输和事务等功能。 WCF中具有一系列的系统已定义的绑定,如BasicHttpBinding、WsHttpBinding、NetTcpBinding等。Binding解决了How to Communicate with Service?的问题。
  • 契约(Contract):契约是对服务操作的抽象,也是对消息交互模式以及消息结构的定义。WCF的契约大体可以分为两类,一类是对服务操作的描述;另一类是对数据的描述。服务契约(Service Contract)则属于对服务操作的描述,而后一类包括其余3中契约:数据契约(Data Contract)、消息契约(Message Contract)和错误契约(Fault Contract)。Contract解决了What function does the Service Provide?的问题。

2.2 WCF 基础概念

  • 消息模式

  消息时一个独立的数据单元,它可能由几个部分组成,包括消息正文和消息头。WCF支持多种消息模式,包括请求-恢复、单向和双工通信。不同传输协议支持不同的消息模式,WCF API和运行库还能保证安全而可靠地发送消息

  • 通信协议和编码

  WCF支持Http、TCP、Peer network(对等网)、IPC(基于命名管道的内部进程通信)和MSMQ协议。在进行消息传递之前,必须对给定的消息进行格式化的编码,WCF提供了3种编码选择:一种是文本编码,一种跨平台的编码;一种是消息传输优化机制(MOMO)编码,该编码用于高效地将非结构化的二进制数据发送到服务或从服务接收这些数据。一种是用于实现高效传输的二进制编码。

  • 行为(Behavior):Behavior的主要作用是定制EndPoint在运行时的一些不要的行为。比如Service回调Client的TimeOut属性的设置。
  • 宿主和宿主进程:服务必须承载于某个进程中,宿主进程是专为承载服务而设计的应用程序,这些宿主进程包括Internet信息服务(即IIS)、Windows激活服务(WAS)、Windows服务。由宿主控制服务的生命周期。
  • 自承载服务:服务除了可以由现有的宿主进程承载外,还可以自承载,自承载服务是由开发人员创建的进程应用程序来承载服务。该应用程序控制服务的声明周期,设置服务的属性和打开服务和关闭服务等操作。

三、创建第一个WCF应用程序

   前面介绍了WCF的详细内容,接下来,我们采用以下两种服务寄宿方法来创建WCF应用程序。

  • 通过自我寄宿(Self-Hosting)的方式,即自承载服务。创建一个控制台应用程序来作为服务的宿主。
  • 通过IIS寄宿方式将服务寄宿在IIS中。客户端通过另一个控制台程序来模拟客户端来对服务进行调用。

  接下来,我们一步一步来使用两种方式来实现我们的WCF应用程序。首先介绍自我寄宿方式的实现步骤。

 步骤一:创建服务契约和服务

  既然是分布式应用程序,首先第一步肯定是创建供其他消费者消费的服务应用程序。而WCF采用基于契约的交互方式实现了服务的自治,以及客户端和服务端之间的松耦合。从功能角度上,服务契约抽象了服务提供的所有操作,所以,我们一般通过接口的形式定义服务契约。WCF通过在接口上应用System.ServiceModel.Service.ServiceContractAttribute特性将一个接口定义成服务契约。在应用该特性的同时,还可以指定服务契约的名称和命名空间,在这个服务契约中,我们将契约名称和命名空间设置成HellworldService和http://www.Learninghard.com 。应用ServiceContractAttribute特性将接口定义成服务契约之后,接口中定义的方法也不能自动成为服务的操作方法。此时,我们需要在相应的方法上显式地应用OperationContractAttribute特性。具体服务契约的实现代码如下所示:

1 [ServiceContract(Name = "HellworldService", Namespace = "http://www.Learninghard.com")]
2     public interface IHelloWorld
3     {
4         [OperationContract()]
5         string GetHelloWorld();
6     }

  服务契约成功创建之后,我们需要实现服务契约来创建具体的WCF服务。WCF服务的具体实现代码如下所示:

1  public class HelloWorldService : IHelloWorld
2     {
3         public string GetHelloWorld()
4         {
5             return "Hello World";
6         }
7     }

步骤二:创建服务宿主

  前面介绍到,WCF服务必须寄存在某个进程中,该进程称为宿主应用程序。服务寄宿的目的就是开启一个进程来为WCF服务提供一个运行的环境。通过为服务添加一个或多个终结点,使之暴露给服务消费者使用。服务消费者再通过相应匹配的终结点对服务进行调用,下面通过创建一个控制台程序来实现WCF服务的自我寄宿方式,具体的实现代码如下所示:

 1 using Contract;
 2 using Services;
 3 using System;
 4 using System.ServiceModel;
 5 using System.ServiceModel.Description;
 6 
 7 namespace Hosting
 8 {
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             using (ServiceHost host = new ServiceHost(typeof(HelloWorldService)))
14             {
15                 // 如果采用配置文件的方式,Region中代码就可以注释点
16                 #region 
17                 host.AddServiceEndpoint(typeof(IHelloWorld), new WSHttpBinding(), "http://127.0.0.1:8888/HelloWorldService");
18                 if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
19                 {
20                     ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
21                     behavior.HttpGetEnabled = true;
22                     behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/HelloWorldService/metadata");
23                     host.Description.Behaviors.Add(behavior);
24                 }
25                 #endregion 
26 
27                 host.Opened += delegate
28                 {
29                     Console.WriteLine("HelloWorldService 已经启动, 按任意键终止服务!");
30                 };
31 
32                 host.Open();
33                 Console.Read();
34             }
35         }
36     }
37 }

  WCF服务寄宿通过ServiceHost对象来完成的。在上面代码中,WCF实例是通过基于WCF服务的类型(typeof(HelloWorldService))来创建的,并通过AddServiceEndpoint添加了一个终结点,具体终结点的地址为http://127.0.0.1:8888/HelloWorldService,采用的绑定(Binding)类型为WSHttpBinding,并指定了服务契约的类型为IHelloWorld。

  WCF是SOA的实现,而SOA的一个基本特征是松耦合,WCF应用中客户端和服务端的松耦合体现在客户端只需了解WCF服务基本的描述,而无需知道具体的实现细节就可以实现对服务的访问。WCF服务的描述通过元数据的形式进行发布的。而WCF元数据的发布是通过一个服务行为ServiceMetadataBehavior来实现的。在上面代码中,我们为创建的ServiceHost对象添加了ServiceMetadataBehavior,并采用了基于Http-GET的元数据获取方式,元数据的发布地址指定为http://127.0.0.1:8888/HelloWorldService/metadata。在调用ServiceHost的Open方法对服务成功寄宿后,你可以通过该地址获取服务相关的元数据,就如Web 服务中通过输入WSDL地址来获得Web服务的描述一样。当我们成功运行ConsoleAppHosting宿主应用程序后,在浏览器中输入http://127.0.0.1:8888/HelloWorldService/metadata这个地址,你将得到如下所示的服务元数据。

  在运行宿主应用程序时,如果你没有以管理员权限运行宿主应用程序的话,你将会得到Http无法注册的异常,具体异常信息如下图所示:

  此时,如果你是在VS中运行宿主程序,你就需要以管理员权限运行VS2012,如果直接在文件夹中运行宿主程序,此时需要右键宿主程序的exe文件,选择以管理员身份运行就不会出现如上图所示的异常。MSDN详细解释连接为:WCF福门教程疑难解答

  而在进行真正的WCF应用开发时,都不会直接通过硬编码的方式进行终结点的添加和服务行为的定义,而是通过配置文件的方式来进行的。你可以以下面配置文件的方式来代替上面的硬编码方式。

<configuration>
    <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="metadataBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8888/HelloWorldService/metadata"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
      <services>
        <service behaviorConfiguration="metadataBehavior" name ="Services.HelloWorldService">
          <endpoint address="http://127.0.0.1:8888/HelloWorldService"
                    binding="wsHttpBinding"
                    contract="Contract.IHelloWorld"/>
        </service>
      </services>
    </system.serviceModel>
</configuration>

步骤三:创建客户端

  服务成功寄宿之后,服务端便开始了服务调用请求的监听工作。另外,服务寄宿将服务描述通过元数据的方式发布出来,相应的客户端就可以获取这些元数据来创建客户端程序来对服务进行调用。在Visual Studio下,当我们添加服务引用时,VS在内部会帮我们实现元数据的获取,并通过代码生成工具(SvcUtil.exe)将这些元数据自动生成用于服务调用的服务代理相关的代码和相应的配置。  

  在成功运行服务寄宿程序后,右键客户端项目,在弹出的菜单中选择“添加服务引用”,然后在弹出的添加服务引用窗口中输入服务元数据的地址:http://127.0.0.1:8888/HelloWorldService/metadata,并指定一个命名空间,点击确定按钮(具体效果如下图所示),VS将为你生成用于服务调用的代理类代码和配置信息。

  添加成功之后,我们可以通过创建服务代理类对象来对服务相应方法进行调用操作,客户端进行服务调用的具体实现代码如下所示:

 1 using Client.HelloWorldServices;
 2 using System;
 3 
 4 namespace Client
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             // HelloworldServiceClient就是VS为我们创建的服务代理类
11             using (HellworldServiceClient proxy = new HellworldServiceClient())
12             {
13                 // 通过代理类来调用进行服务方法的访问
14                 Console.WriteLine("服务返回的结果是: {0}", proxy.GetHelloWorld());
15             }
16 
17             Console.Read();
18         }
19     }
20 }

  运行客户端程序后,你将获得如下图所示的运行结果。

 

  上面演示了通过自我寄宿的方式来寄宿服务,接下来我们来演示如何将WCF服务寄宿到IIS中。因为WCF服务和服务契约在上面方式中已实现,所以IIS寄宿方式的包含两个步骤:创建IIS宿主服务和创建客户端调用程序。下面分别介绍下这两个步骤。

步骤一:创建IIS宿主服务

  在开始的解决方案中,创建一个Asp.net空Web应用程序,然后添加一个WCF服务文件。这里WCF服务文件与Web 服务中的.asmx文件类似。基于IIS的服务寄宿要求相应的WCF服务具有相应的.svc文件,.svc文件部署于IIS站点中,对WCF服务的调用体现在对.svc文件的访问上。

 WCF服务文件的内容很简单,仅仅包含一个ServiceHost指令,该指令具有一个必须的Service属性和一些可选的属性。详细信息见MSDN:@ServiceHost。所以对应的.svc文件内容如下所示:

<%@ ServiceHost Language="C#" Debug="true" Service="Services.HelloWorldService" %>

具体Web.Config的配置内容如下所示:

<configuration>
  <system.web>
    <compilation debug="true"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataBehavior">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="metadataBehavior" name ="Services.HelloWorldService">
        <endpoint binding="wsHttpBinding"
                  contract="Contract.IHelloWorld"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

  从上面配置内容可以看出,这基本上和自我寄宿方式的配置文件一致,唯一不同的是在添加终结点中无需指定地址,因为.svc所在的地址就是服务的地址。

步骤二:创建客户端程序

  此时,客户端仅仅需要修改终结点地址来对寄宿于IIS下的HellworldService进行访问,该地址为:http://localhost:15826/HelloWorldService.svc。此时可以http://localhost:15826/HelloWorldService.svc?wsdl得到相应的元数据。具体客户端代码的实现如下所示:

 1 using Client2.HelloWorldService;
 2 using System;
 3 
 4 namespace Client2
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             using (HellworldServiceClient proxy = new HellworldServiceClient())
11             {
12                 Console.WriteLine("服务返回的结果是: {0}", proxy.GetHelloWorld());
13             }
14 
15             Console.Read();
16         }
17     }
18 }

  具体的配置文件内容如下所示:

<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_HellworldService" />
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:15826/HelloWorldService.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_HellworldService"
                contract="HelloWorldService.HellworldService" name="WSHttpBinding_HellworldService">
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

  运行客户端2,得到的运行结果与自我寄宿方式得到的结果一样。这里就不贴出结果图了。

四、总结

  到这里,本篇文章的分享就结束。本文首先通过介绍WCF相关的基础概念,其中最重要的莫过于终结点和组成它的三个元素,之后分别介绍自我寄宿和IIS寄宿方式来创建WCF应用程序,在平常开发过程中,用到最多是通过IIS寄宿方式来对服务进行寄宿。在一篇文章中将分享关于WCF服务契约的内容。

   本文所有源代码下载地址:FirstWCFApp.zip

 

posted @ 2014-10-19 16:09  Learning hard  阅读(...)  评论(...编辑  收藏