WCF的简单应用

面向服务架构(SOA,Service Orientation Architecture)就近年来备业界关注的一个主题,它代表了软件架构的一种方向。面向服务代表的是一种设计理念,和面向对象(Object Orientation)、面向组件(Component Orientation)一样,体现的是一种对关注点进行分解的思想,面向服务是和技术无关的。Web服务(这里指的是广义的Web服务,既包括微软平台下面的ASP.Net.asmxWeb服务和WCF,也包括其他平台的Web服务)是一种实现SOA理想的技术手段。服务的自治原则要求单个服务在底层逻辑控制方面尽可能是独立和自包含的,服务尽可能不依赖于访问它的客户端和其他服务。服务可以独立地进行部署及实施版本策略和安全策略。SOA有以下一些基本的特性:1.SOA依赖于开放的标准;2.SOA支持跨平台;3.SOA鼓励创建可组合的服务;4.SOA鼓励服务的复用;5.SOA强调松耦合。建立一个完全基于SOA的通信框架是微软推出WCF的主要动机。在过去的若干年中,微软先后推出了一系列的分布式通信技术,比较典型的包括COM/DCOM、Enterpise Service、.Net Remoting、XML Web服务、MSMQ等。而WCF的作用就是将以上所有分布式通信技术整合统一起来,提供一个统一的应用编程接口(API)。

WCF合并了ASP.NET Web服务、.NET Remoting、消息队列和Enterprise Services的功能,WCF的功能包括:

1.存储组件和服务——与联合使用自定义主机、.NET Remoting和WSE一样,也可以将WCF服务放在ASP.NET运行库、Windows服务、COM+进程或Windows窗体应用程序中,进行对等计算。

2.声明行为——不要求派生自基类,而可以使用属性定义服务。这类似于ASP.NET开发的Web服务。

3.通信信道——在改变通信信道方面,.NETRemoting非常灵活,WCF也不错,因为它提供了相同的灵活性。WCF提供了HTTP、TCP和IPC信道进行通信的多条信道,也可以创建使用不同传输协议的自定义信道。

4.安全结构——为了实现独立于平台的Web服务,必须使用标准化的安全环境。所提出的标准用WSE3.0实现,这在WCF中被继承下来。

5.可扩展性——.NET Remoting有丰富的扩展功能。它不仅能创建自定义信道、格式化程序和代理,还能将功能注入客户端和服务器上的消息流。WCF提供了类似的可扩展性。但是,WCF的扩展性用SOAP标题创建。

6.支持以前的技术——要使用WCF根本不需要完全重写分布式解决方案,因为WCF可以与已有的技术集成。WCF提供的信道使用DCOM与服务组件通信,用ASP.NET开发的Web服务也可以与WCF集成。

WCF的最终目标是通过进程或不同的系统,通过本地网络或通过Internet收发客户和服务之间的消息。如果需要以独立于平台的方式尽快收发消息,就应该这么做。在远程视图上,服务提供了一个端点,它用协定、绑定和地址来描述。协定定义了服务提供的操作,绑定给出了协议和编码信息,地址是服务的位置。客户需要一个兼容的端点来访问服务。

客户调用代理上的一个方法。代理提供了服务定义的方法,但把方法调用转换为一条消息,并把该消息传输到信道上。信道有一个客户端部分和一个服务器端部分,它们通过一个网络协议来通信。在信道上,把消息传递给调度程序,调度程序再把消息转换为用服务调用的方法调用。

WCF支持几个通信协议。为了进行独立于平台的通信,需要支持Web服务标准。要在.NET应用程序之间通信,可以使用较快的通信协议,其系统开销最小。

独立于平台的通信的核心服务的功能有以下几个方面。

1.SOAP——Simple Object Access Protocol ,意思是简单对象访问协议,这是一个独立于平台的协议,它是几个Web服务规范的基础,支持安全性、事务和可靠性。为了进行独立于平台的通信,可以使用SOAP协议,它得到WCF的直接支持。SOAP最初是Simple Object Access Protocol的缩写,但自从SOAP1.2以来,SOAP不再是一个对象访问协议,因为可以发送用XML架构定义的消息。

2.WSDL——Web Service Description Language,意思是Web服务描述语言,这是提供服务的元数据。WSDL文档描述了服务的操作和消息,定义了服务的的元数据,这些元数据可用于为客户端应用程序创建代理。WSDL包含如下信息:

消息的类型:用XML架构描述

从服务中收发的消息:消息的各部分是XML架构定义的类型

端口类型:映射服务协定,列出了用服务协定定义的操作。操作包含消息,例如,与请求和响应序列一起使用的输入的输出信息

绑定信息:包含用端口类型列出的操作和用SOAP变体定义的操作

服务信息:把端口类型映射到端点地址

3.REST——Representational State Transfer,意思是代表性状态传输,由支持REST的Web服务用于在HTTP上通信。WCF提供了使用REST进行通信的功能。REST并不是一个协议,但定义了一使用服务访问资源的几条规则。支持REST的Web服务是基于HTTP协议和REST规则的简单服务。规则按3个类别来定义:可以用简单的URI访问服务,支持MIME类型,以及使用不同的HTTP方法。支持MIME类型,就可以从服务中返回不同的数据格式,如普通XML、JSON或AtomPub。HTTP请求的GET()方法从服务中返回数据。其他方法有PUT()、POST()或DELETE()。PUT()方法用于更新服务端,POST()方法可创建一个新资源,DELETE()方法删除资源。REST允许给服务发送的请求比SOAP小。如果不需要SOAP提供的事务、安全消息和可靠性,则利用REST构建的服务可以减小系统开销。使用REST体系结构时,服务总是无状态的,服务的响应可以缓存。

4.JSON——JavaScript Object Notation,意思是Java对象标记,便于在Java客户端上使用。除了发送SOAP消息之外,从JavaScript中访问服务最好使用JSON。JSON的系统开销比SOAP小,因为它不是XML,而是为JavaScript客户端进行了优化。这使之非常适用于从Ajax客户端使用。JSON没有提供通过SOAP标题发送所具备的可靠性、安全性和事务性,但这些通常是JavaScript客户端不需要的功能。

下面我们来构建一个简单的WCF应用。这个应用功能简单(实现四则运算),但却具有一个完整的WCF应用的基本结构。我们可以建立如下图所示的解决方案和项目:

这四个项目的类型、承载的功能和相互引用关系如下:

Service.Interface:用于定义服务契约(Service Contract)的类库项目,引用WCF的核心程序集System.ServiceModel.dll。

Service:用于定义服务类型的类库项目。由于服务类型需要实现定义在Service.Interface中相应的契约接口,因此该项目具有对Service.Interface项目的引用。

Hosting:作为服务宿主的控制台应用。该项目同时引用Service.Interface、Service项目和System.ServiceModel.dll程序集。

Client:一个控制台应用模拟服务的客户端,该项目引用System.ServiceModel程序集。

从功能上讲,服务契约抽象了服务提供的所有操作;而站在消息交换来看,服务契约则定义了基于服务调用的消息交换过程中请求消息和回复消息的结构,以及采用的消息交换模式,我们一般将服务契约定义成接口。服务契约Service.Interface的代码如下所示,我们通过将System.ServiceModel.ServiceContractAttribute特性应用在接口ICalculator上从而将其定义成了服务契约。在应用ServiceContractAttribute特性的同时,还可以指定服务契约的名称(CalculatorService)和命名空间(http:www.artech.com/)。

namespace Service.Interface
{
    //通过将System.ServiceModel.ServiceContractAttribute特性应用在接口ICalculator上从而
    //将接口定义成服务契约
    [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);   //除法
    }
}
通过应用ServiceContractAttribute特性将接口定义成服务契约之后,接口的方法并不能自动成为服务操作。WCF采用的是显式选择(Explicit Opt-in)的策略,意味着我们需要在相应的操作方法上面显式地应用OperationContractAttribute特性。

当契约接口ICalculator成功创建后,我们在Service项目中创建实现它的服务CalculatorService。如下代码实现了该接口的方法。

namespace Service
{
    /// <summary>
    /// 计算器服务类,继承自ICalculator接口
    /// </summary>
    public class CalculatorService : ICalculator
    {
        /// <summary>
        /// 实现加法方法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>两数之和</returns>
        public double Add(double x, double y)
        {
            return x + y;
        }

        /// <summary>
        /// 实现减法方法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>两数之差</returns>
        public double Subtract(double x, double y)
        {
            return x - y;
        }

        /// <summary>
        /// 实现乘法方法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>两数之积</returns>
        public double Multiply(double x, double y)
        {
            return x * y;
        }

        /// <summary>
        /// 实现除法方法
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>两数之商</returns>
        public double Divide(double x, double y)
        {
            return x / y;
        }
    }
}
WCF的服务不能孤立地存在,需要寄宿一个运行着的进程中,我们把承载WCF服务的进程称之为宿主,为服务指定宿主的过程称为服务寄宿(Service Hosting)。我们采用自我寄宿和IIS寄宿两种方式。对于前者,我们通过一个控制台应用作为宿主;对于后者,则将服务寄宿于IIS的工作进程(W3WP.exe)。客户端通过另一个控制台应用模拟。

WCF服务需要依存一个运行着的宿主进程,服务寄宿就是为服务指定一个宿主的过程。WCF采用基于终结点(Endpoint)的通信手段。终结点由地址(Address)、绑定(Binding)和契约(Contract)三要素组成。由于三要素的首字母分别为A、B、C,所以就有了易于记忆的公式:Endpoint = ABC。

一个终结点包含了通信所必需的所有信息,它的三要素的具体功能如下:

地址(Address):地址决定了服务的位置,解决了服务寻址的问题。

绑定(Binding):绑定实现了通信的所有细节,包括网络传输、消息编码,以及其他为实现某种功能(比如传输安全、可靠消息传输、事务等)对消息进行的相应处理。WCF中具有一系列的系统定义绑定,比如BasicHttpBinding、WSHttpBinding和NetTcpBinding等。

契约(Contract):契约是对服务操作的抽象,也是对消息交换模式的定义及消息结构的定义。

服务寄宿的目的就是开启一个进程,为WCF服务提供一个运行环境,并为服务添加一个或多个终结点,使之暴露给潜在的服务消费者。在Hosting项目中,我们编写了如下的代码将服务CalculatorService寄宿于控制台程序中:

namespace 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:3721/calculatorservice");
                if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:3721/calculatorservice/metadata");
                    host.Description.Behaviors.Add(behavior);
                }
                host.Opened += delegate
                {
                    Console.WriteLine("CalculatorService已经启动,按任意键终止服务!");
                };
                host.Open();
                Console.Read();
            }
        }
    }
}
WCF服务寄宿通过System.ServiceModel.ServiceHost对象完成。在以上代码中,我们基于服务类型(typeof(CalculatorService))创建了ServiceHost对象,并添加了一个基于WSHttpBinding绑定的终结点。该终结点的地址为http://127.0.0.1:3721/calculatorservice,并指定了服务契约的类型ICalculator。127.0.0.1是回送地址,指本地机,一般用来测试使用。回送地址(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。

松耦合是SOA的一个基本特征,WCF应用中客户端和服务端的松耦合体现在客户端只需要了解WCF服务基本的描述,而无须知道具体的实现细节,就可以实现正常的服务调用。WCF服务通过元数据(Metadata)的形式发布出来。WCF中元数据的发布通过一个特殊的服务行为ServiceMetadataBehavior来实现。

在上面提供的服务寄宿代码中,我们为ServiceHost添加了ServiceMetadataBehavior这样一个服务,并采用了基于HTTP-GET的元数据获取方式,并且通过ServiceMetadataBehavior的HttpGetUrl属性指定元数据发布地址(http://127.0.0.1:3721/calculatorservice/metadata)。在服务成功寄宿后,可以通过该地址获取服务相关的元数据。如果在IE地址栏中输入该地址,将会看到如下图所示的以WSDL形式表示的服务元数据。

在进行真正的WCF应用开发时,一般采用配置而不是编程的方式进行终结点的添加和服务行为的定义。上面的添加终结点和定义服务行为的代码可以用下面的配置代替。

<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataBehavior">
          <serviceMetadata httpGetEnabled="true" 
                           httpGetUrl="http://127.0.0.1:3721/calculatorservice/metadata"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WcfService.Service.CalculatorService" 
               behaviorConfiguration="metadataBehavior">
        <endpoint address="http://127.0.0.1:3721/calculatorservice" 
                  binding="wsHttpBinding" 
                  contract="WcfService.Service.Interface.ICalculator"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>
对于初学者来说,WCF的配置显得过于复杂,直接对配置文件进行手工编辑不太现实。我们可以直接使用Visual Studio提供的配置工具。可以单击“工具(Tools)”菜单,选择“WCF服务配置编辑器(WCF Service Configuration Editor)”菜单项,开启WCF服务配置编辑器。

如果采用了上述配置,服务寄宿代码将得到极大的精简。由于我们将终结点和用于元数据发布的ServiceMetadataBehavior服务行为都定义在配置中,因此在进行服务寄宿的时候,只需要直接根据服务类型创建ServiceHost并调用Open方法将其开启就可以了。代码如下:

static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            {               
                host.Opened += delegate
                {
                    Console.WriteLine("CalculatorService已经启动,按任意键终止服务!");
                };
                host.Open();
                Console.Read();
            }
        }
当服务被成功寄宿后,服务端便开始了服务调用请求的监听。当通过Visual Studio添加服务引用的时候,Visual Studio在内部帮助我们实现元数据的获取,并借助这些元数据通过代码生成工具(SvcUtil.exe)自动生成服务调用的服务代理相关代码和相应的配置。

在运行服务寄宿程序(Hosting.exe)的情况下,右键单击Client项目并在弹出的快捷菜单中选择“添加服务引用”,会弹出如下所示的添加服务引用对话框。在地址栏中输入服务元数据发布的源地址(http://127.0.0.1:3721/calculatorservice/metadata),并指定一个命名空间,单击“确定”按钮,Visual Studio会生成一系列用于服务调用的代码和配置。

然后在Client项目中可以直接利用通过添加服务引用生成的服务代理类来调用寄宿的CalculatorService服务。代码如下:

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            using (CalculatorServiceClient proxy = new CalculatorServiceClient())
            {
                Console.WriteLine("{0} + {1} = {2}", 1, 2, proxy.Add(1, 2));
                Console.WriteLine("{0} - {1} = {2}", 1, 2, proxy.Subtract(1, 2));
                Console.WriteLine("{0} * {1} = {2}", 1, 2, proxy.Multiply(1, 2));
                Console.WriteLine("{0} / {1} = {2}", 1, 2, proxy.Divide(1, 2));
            }
            Console.ReadKey();
        }
    }
}
在服务寄宿程序Hosting开启的情况下运行客户端程序Client,运行结果如下:


上面的示例是将控制台应用作为服务宿主的自我寄宿方式。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值