当前位置: 首页 > 文章教程  > 计算机与互联网 > 网络编程

16.3WCF核心元素

8/31/2020 9:19:23 PM 人评论

16.3WCF核心元素

16.3 WCF核心元素

WCF框架中包含大量的基础概念,在16.2节中创建了一个简单的WCF程序,本节将对前面出现的WCF核心概念进行详细介绍,如WCF地址、绑定和合约等。

16.3.1 地址

WCF的每一个服务都具有一个唯一的地址(Address)。每个地址都包含两个重要元素:服务位置与传输协议,或者是用于服务通信的传输方式。服务位置包括目标主机名、站点(或者网络)、通信端口、管道(或者队列),以及一个可选的特定路径或者URI。

总地来说,WCF支持如下几种传输方式:HTTP,TCP,Peer Network(对等网),IPC(基于命名管道的内部进程通信),MSMQ。

地址通常采用如下格式。

    [传输协议]://[主机名或域名][:可选端口]

例如,如下所示的这些都是正确的地址。

    http://localhost:8080
    http://localhost:5678/MyWcfService
    net.tcp://localhost:1010/MyWcfService
    net.pipe://localhost/MyWcfPipe
    net.msmq://localhost/public/MyWcfService
    net.msmq://localhost/MyWcfService

如上述示例所示,可以将地址“http://localhost:8080”理解为“采用HTTP访问localhost主机,并在8080端口等待用户的调用”。对于“http://localhost:5678/MyWcfService”地址则可以理解为“采用HTTP访问localhost主机,MyWcfService服务在5678端口处等待用户的调用。”

1.TCP地址

TCP地址采用net.tcp作为协议进行传输,通常它还带有端口号。例如:

    net.tcp://localhost:8018/MyWcfService

如果没有指定端口号,则TCP地址的默认端口号为808。例如:

    net.tcp://localhost/MyWcfService

另外,两个TCP地址(来自于相同的主机)可以共享一个端口。例如:

    net.tcp://localhost:8018/MyWcfService
    net.tcp://localhost:8018/MyOtherWcfService

2.HTTP地址

HTTP地址是使用频率最高的一种地址,它使用HTTP进行传输,也可以利用HTTPS进行安全传输。HTTP地址通常会被用作对外的基于Internet的服务,并为其指定端口号。例如:

    http://localhost:8088
    http://localhost:8088/MyWcfService
    https://localhost/MyWcfService

如果没有指定端口号,则默认为80。与TCP地址相似,两个相同主机的HTTP地址可以共享一个端口,甚至相同的计算机。

3.IPC地址

IPC地址使用net.pipe协议进行传输,这意味着它将使用Windows的命名管道机制。在WCF中,使用命名管道的服务只能接收来自同一台计算机的调用。

因此,在使用时必须明确指定本地计算机名或者直接命名为localhost,然后再为管道名提供一个唯一的标识字符串。例如:

    net.pipe://localhost/MyWcfPipe
    net.pipe://zhht/MyWcfPipe

注意

每台计算机只能打开一个命名管道。因此,两个命名管道地址在同一台计算机上不能共享一个管道名。

4.MSMQ地址

MSMQ地址使用net.msmq协议进行传输,即使用了微软消息队列(Microsoft Message Queue)机制。在使用时必须为MSMQ地址指定队列名。如果是处理私有队列,则必须指定队列类型。例如:

    net.msmq://localhost/private/MyWcfService
    net.msmq://localhost/private/MyOtherWcfService

但对于公有队列而言,队列类型可以省略。例如:

    net.msmq://localhost/MyWcfService
    net.msmq://zhht/private/MyWcfService

5.对等网地址

对等网地址(Peer Network Address)使用net.p2p协议进行传输,它使用了Windows的对等网传输机制。如果没有使用解析器,就必须为对等网地址指定对等网名称、唯一的路径以及端口。

16.3.2 绑定

WCF中的绑定(Binding)指定了服务的通信方式。

使用绑定也是WCF开发区别于Web服务开发的一个重要方面。因为WCF带有许多可供选择的绑定,每种绑定都适合于特定的需求。另外,如果现有的绑定类型不能满足需求,还可以通过扩展CustomBinding类型创建绑定。

简单来说,WCF绑定可以指定如下特性。

(1)传输协议。

(2)安全要求。

(3)编码格式。

(4)事务处理要求。

一个绑定类型包含多个绑定元素,它们描述了上面所有的绑定要求。WCF内置了9种类型的绑定,如表16-2所示列出了这些绑定类型及其说明。

表16-2 WCF绑定类型

提示

所有以Net前缀开始的绑定类型都使用二进制编码在.NET应用程序之间通信,这种编码格式比文本格式要快。

在表16-2中列出的每种绑定类型都有自己的特性。例如,以WS为前缀的绑定类型是独立于平台的,支持Web服务规范。以Net为前缀的使用二进制格式,使.NET应用程序之间的通信具有更高的性能。如表16-3所示针对这些绑定类型按特性进行了分类。

表16-3 绑定类型支持的特性

除了定义绑定之外,WCF服务还必须定义端点。端点依赖于合约、服务的地址和绑定。例如,在下面的示例代码中,实例了一个ServiceHost对象,将地址“http://localhost:8080/MyWcfService”和一个WSHttpBinding实例绑定到服务的一个端点上。

    static void StartService()
    {
        ServiceHost host;
        Uri baseAddress = new Uri("http://localhost:8080/MyWcfService");
        host = new ServiceHost(typeof(Service1));
        WSHttpBinding binding = new WSHttpBinding();
        host.AddServiceEndpoint(typeof(Service1), binding, baseAddress);
        host.Open();
    }

除了以编程方式定义绑定之外,还可以在应用程序的配置文件中定义它。WCF的所有配置都位于<system.serviceModel>节点中,<service>节点定义了WCF中所提供的服务,<bindings>节点定义了绑定信息。

例如,下面的配置文件同样实现了上述代码的功能。

    <?xml version="1.0" encoding="utf-8" ? >
    <configuration>
      <system.serviceModel>
        <services>
          <service name="FirstWcfServiceLibrary.Service1">
          <host>
            <baseAddresses>
              <add baseAddress = "http://localhost:8080/MyWcfService" />
            </baseAddresses>
          </host>
          <endpoint address ="" binding="wsHttpBinding" contract="FirstWcfServiceLibrary.IService1" bindingConfiguration="config1"/>
          </service>
        </services>
        <bindings>
          <wsHttpBinding>
            <binding name="config1">
              <reliableSession enabled="true"/>
            </binding>
          </wsHttpBinding>
        </bindings>
      </system.serviceModel>
    </configuration>

可以看到,一个WCF服务必须要有一个端点,该端点包含地址、绑定和合约信息。WSHttpBinding的默认配置由bindingConfiguration属性指定,该属性引用了下方名为“config1”的绑定配置信息。该配置信息位于<bindings>节点中,并启用了reliableSession。

16.3.3 合约

任何一个分布式应用程序,之所以能够互相传递消息,都是事先制定好数据交换规则的,这个规则正是交换数据的双方(比如服务器端和客户端)能彼此理解对方的依据。WCF作为分布式开发技术的一种,同样具有这样一种特性。而在WCF中制定的规则就被称为合约(Contract),它是WCF的消息标准,是任何一个WCF程序不可或缺的一部分。

在WCF中合约分为4种,分别为:定义服务操作的服务合约(Service Contract),定义自定义数据结构的数据合约(Data Contract),定义错误异常的异常合约(Fault Contract),以及直接控制消息格式的消息合约(Message Contract)。

1.服务合约

一般情况下,用接口(Interface)来定义服务合约。虽然也可以使用类(Class)来定义,但使用接口的好处更明显一些。主要表现在如下方面。

(1)便于合约的继承,不同的类型可以自由实现相同的合约。

(2)同一服务类型可以实现多个合约。

(3)和接口隔离原则相同,随时可以修改服务类型。

(4)便于制定版本升级策略,让新老版本的服务合约同时使用。

服务合约定义了WCF服务可以执行的操作,它包括ServiceContract和OperationContract两种。ServiceContract用于类或者接口上,用于指定此类或者接口能够被远程调用,而OperationContract用于类中的方法上,用于指定该方法可被远程调用。

例如,下面的示例代码使用ServiceContract属性声明接口IMyFirstService可以被远程调用,OperationContract属性声明getTime()方法也可以被远程调用。

    [ServiceContract]
    public interface IMyFirstService
    {
        [OperationContract]
        string getTime();
    }

在表16-4中列出了ServiceContract属性的可用选项及其说明。

表16-4 ServiceContract属性的选项及说明

在表16-5中列出了OperationContract属性的可用选项及其说明。

表16-5 OperationContract属性的选项及说明

提示

在服务合约中,还可以使用DeliveryRequirements属性定义服务的传输要求;使用RequireOrderedDelivery属性指定所传递的消息必须以相同的顺序到达;使用QueuedDeliveryRequirements属性指定消息以断开连接的方式传送。

2.数据合约

数据合约也分为两种:DataContract和DataMember。DataContract用于类或者结构上,指定此类或者接口能够被序列化并传输,而DataMember只能用在类或者接口的属性(Property)或者字段(Field)上,指定该属性或者字段能够被序列化传输。

数据合约的序列化不同于普通.NET的序列化机制,在运行时所有的字段(包括私有字段)都会被序列化,而在执行数据合约的序列化时只有被标记了DataMember的属性才会被序列化。

例如,下面创建一个Person类并使用DataContract属性指定为可序列化。另外还创建了一个服务合约并定义了一个Add()方法接收一个Person类型的参数。

    [DataContract]
    public class Person
    {
        [DataMember]
        public int Id;
        [DataMember]
        public string Name;
        [DataMember]
        public DateTime Birthday;
        [DataMember]
        public string Email;
    }
    [ServiceContract]
    public interface IPerson
    {
        [OperationContract]
        bool Add(Person p);
    }

如表16-6所示列出了DataMember属性可用的选项及其说明。

表16-6 DataMember属性的选项及说明

例如,要对Order对象进行序列化,它的定义如下。在这里使用Namespace选项重新定义了命名空间。对于数据成员使用Name选项指定了一个别名,使用Order选项指定了显示的顺序。

    [DataContract(Namespace = "http://www.itzcn.com")]
    public class Order
    {
        [DataMember(Name="OrderId", Order=1)]
        public Guid ID;
        [DataMember(Name="OrderDate", Order=2)]
        public DateTime Date;
        [DataMember]
        public string Customer;
        [DataMember]
        public string Address;
        public double TotalPrice;
    }

执行后,Order对象的序列化XML如下所示。

    <Order xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.itzcn.com">
        <Address></Address>
        <Customer></Customer>
        <OrderId></OrderId>
        <OrderDate></OrderDate>
    </Order>

通过定义的数据合约以及与最终生成XML结构的对比,可以总结出WCF默认采用如下的数据合约序列化规则。

(1)XML的根节点为数据合约中类的名称,默认命名空间格式为http://schemas.datacontract.org/2004/07/{类所在命名空间}。

(2)只有使用DataMember属性定义的字段或者属性才能作为数据成员参与序列化(例如本实例中的TotalPrice属性不会再现在序列化后的XML中)。

(3)所有数据成员均以XML元素的形式被序列化。

(4)默认数据成员按照字母顺序排列。

(5)如果通过Order指定了顺序,且值相同,则以字母先后顺序排列。

(6)未指定Order的成员顺序在指定Order顺序之前。

(7)如果DataContract处于继承的类中,那么将优先显示父类中的成员。

3.消息合约

如果需要在WCF服务中对SOAP消息进行控制则必须使用消息合约。在消息合约中,可以指定消息的哪些部分出现在SOAP标题中,哪些部分要放在SOAP的主体中。

例如,下面的示例演示了使用ProcessOrderMessage类定义消息合约的代码。

    [MessageContract]
    public class ProcessOrderMessage
    {
        [MessageHeader]
        public Guid ID;
        [MessageBodyMember]
        public Order order;
    }

如上述代码所示,在这里使用MessageContract属性指定ProcessOrderMessage为一个消息合约,对于SOAP消息中的标题使用MessageHeader属性指定,SOAP主体使用MessageBodyMember属性指定。

为了使用上面定义的消息合约,这里将IProcessOrder接口定义为服务合约,并定义了一个可调用的ProcessOrder()方法。代码如下所示。

    [ServiceContract]
    public interface IProcessOrder
    {
        [OperationContract]
        ProcessOrderMessage ProcessOrder(ProcessOrderMessage message);
    }

4.异常合约

在WCF中所有合约基本上都是围绕着一个服务调用时的消息交换来进行的。例如,服务的客户端通过向服务的提供者发送请求消息;服务提供者在接收到该请求后激活服务实例,并调用相应的服务操作;最终将返回的结果以回复消息的方式返回给服务的客户端。

但是,如果服务操作不能正确地执行,服务端将会通过一种特殊的消息将错误信息返回给客户端,这种消息被称为异常消息。对于异常消息,同样需要相应的合约来定义其结构,这种合约称为异常合约(Fault Contract)。

WCF通过FaultContract属性来定义异常合约。由于异常合约是基于服务操作级别的,所以该属性将直接应用于服务合约接口或者操作合约的方法上。

下面的示例代码演示了FaultContract定义异常合约的方式。

    [ServiceContract]
    public interface IUser
    {
        [OperationContract]
        [FaultContract(typeof(LoginTimeOut))]
        bool Login(string username, string userpass);
    }

在上述代码中,使用FaultContract属性声明调用Login()方法会抛出LoginTimeOut类的异常表示登录超时。

与本节前面介绍的其他合约一样,异常合约的FaultContract属性也有很多可用选项,如表16-7中列出了它们及其说明。

表16-7 FaultContract属性的选项及说明

上一篇:16.1WCF概述

下一篇:16.4端点

相关教程

共有条评论 网友评论

验证码: 看不清楚?