WCF初探-15:WCF操作协定

前言:

  • 在前面的文章中,我们定义服务协定时,在它的操作方法上都会加上OperationContract特性,此特性属于OperationContractAttribute 类,将OperationContract应用于方法,以指示该方法实现作为服务协定(由 ServiceContractAttribute 属性指定)一部分的服务操作。OperationContractAttribute 属性声明方法是服务协定中的操作。 只有具有 OperationContractAttribute 属性的方法可作为服务操作公开。 不含有 OperationContractAttribute 标记的所有方法的服务协定不公开任何操作。公开的操作方法我们称之为操作协定。

操作协定的属性:

  OperationContract修饰的操作协定具有以下参数:

  • Action: 获取或设置请求消息的 WS-Addressing 操作。 WCF 根据请求消息的操作将它们调度至方法。
  1. 使用 Action 属性控制方法的输入消息的操作。 由于 WCF 使用该操作将传入消息调度至相应方法,因此在协定操作中使用的消息必须具有唯一的操作。 默认操作值由以下几项组成:协定命名空间(默认值为“http://tempuri.org/”)、协定名称(如果没有使用显式服务接口,则为接口名称或类名)、操作名称,并且如果该消息是一个相关的响应,则还有一个附加字符串(“Response”)。 您可以使用 Action 属性重写该默认值。
  2. 若要指示服务操作可处理该服务接收的所有消息,但又不能定向到服务操作,请指定值“*”(星号)。 这种类型的操作(称为不匹配的消息处理程序)必须具有下列方法签名之一,否则会引发 InvalidOperationException :该服务操作只能接受一个 Message 对象,并且返回一个 Message 对象;该服务操作只能接受一个 Message 对象,并且不返回任何内容(即返回 void)。
  3. 服务协定只能有一个 Action 属性设置为“*”的服务操作。 当 IsInitiating 属性设置为 false 时,服务类实现的相同 listenUri 所承载的任何服务协定组可具有多个 Action 属性设置为“*”的服务操作。 然而,其中只有一个服务操作可将 Action 属性设置为“*”,并将 IsInitiating 属性设置为 true。
  • AsyncPattern :属性指示使用 Begin/End 方法对可以实现或异步调用该操作。
  1. 使用 AsyncPattern 属性生成可在服务器和/或客户端异步调用的服务操作。 AsyncPattern 属性通知运行库 Begin 方法有一个符合 .NET Framework 异步方法设计模式的匹配的 End 方法。 生成用以实现服务操作的服务器异步方法可增强服务器的可伸缩性和性能,而不会影响服务的客户端。如果服务操作在执行完可异步执行的较长操作后,必须将某些内容返回至客户端,建议使用此方法。
  2. 这不会影响客户端,因为服务器上的异步方法对是实现详细信息,该信息不会影响操作的基础 Web 服务描述语言 (WSDL) 描述。 此类方法在客户端显示为包含 <input> 和相关 <output> 消息的单个操作。 WCF 自动将入站消息路由至 Begin<methodName> method and routes the results of the End<methodName> 调用的结果路由至出站消息。 因此,客户端信道可将方法对表示为单个同步操作或一个异步操作对。 客户端表示形式在任何情况下都不会以任何方式影响服务器上的异步实现。
  3. 客户端协定可使用 AsyncPattern 属性指示异步方法对,即客户端可使用该方法对异步调用操作。 通常,客户端应用程序使用 ServiceModel 元数据实用工具 (Svcutil.exe) 工具和 /async 选项生成客户端可以调用使用异步操作的 Begin<methodName> 和 End<methodName> 方法对。
  4. 如果服务操作具有异步和同步两个版本,则服务上的默认行为是调用同步版本。
  • IsOneWay :获取或设置一个值,该值指示操作是否返回答复消息。
  1. 使用 IsOneWay 属性可以指示操作不返回答复消息。 这种类型的操作对通知或事件样式通信十分有用,特别是双向通信。 如果不等待基础响应消息,则单向操作的调用方在处理请求消息时无法直接检测错误。
  2. 在面向双工(或双向)服务的应用程序中,客户端和服务器相互独立地通信,并且客户端信道可使用其方法中的 IsOneWay 属性指示服务可单向调用客户端(该客户端可作为事件处理)。 这不会返回调用或生成消息,因为该服务不需要任何响应消息。
  3. 如果 IsOneWay 属性设置为 false(默认值),即使返回 void 的方法也会生成答复消息。 在此种情况下,基础结构将创建并发送一条空消息,以向调用方指示该方法已返回内容。(通过此方法使基础结构可以将 SOAP 错误发送回客户端。)将 IsOneWay 设置为 true 是取消创建和调度响应消息的唯一方法。
  4. 单向方法不得返回一个值或具有 ref 或 out 参数;否则将引发 System.InvalidOperationException 异常。
  5. 指定操作是单向操作,只表示它没有响应消息。 如果无法建立连接、出站消息非常大或该服务无法足够快地读取入站信息,则可能会阻止。 如果客户端要求非阻止调用,则会生成 AsyncPattern 操作。
  • IsInitiating: 获取或设置一个值,该值指示方法是否实现可在服务器上启动会话(如果存在会话)的操作。
  1. ServiceContractAttribute.SessionMode 的值必须为 Allowed 或 Required 且使用的绑定必须要求或允许会话,IsInitiating 属性才能正常工作。
  2. 默认为 true,这意味着操作可以是通道上调用的第一个操作。 除了调用该方法之外,后续的调用对于启动方法无效。 不会创建其他任何会话。 如果协定不使用会话,则将 IsInitiating 设置为 false 会被忽略。通常,将 IsInitiating 设置为 false 可强制客户端在调用此方法之前,调用服务上的另一个方法。
  3. IsInitiating 和 Action 属性之间存在交互操作。 服务协定只能有一个 Action 属性设置为“*”的服务操作。 当 IsInitiating 属性设置为 false 时,服务类实现的相同侦听 URI 所承载的任何服务协定组可具有多个 Action 属性设置为“*”的服务操作。 然而,其中只有一个服务方法可将 Action 属性设置为“*”,并将 IsInitiating 属性设置为 true。
  4. 如果服务收到非启动操作的消息,则该服务返回 ActionNotSupported SOAP 错误。 客户端将这种情况当作 FaultException。 如果客户端首先调用非启动操作,则客户端运行库会引发 System.InvalidOperationException。
  • IsTerminating: 获取或设置一个值,该值指示服务操作在发送答复消息(如果存在)后,是否会导致服务器关闭会话。
  1. 使用 IsTerminating 属性指示调用服务操作可终止通信会话。
  2. 在客户端应用程序中,将 IsTerminating 值设置为 true 以指示 WCF 在答复到达后,关闭信道。
  3. 在服务中,如果客户端在该期间内不关闭信道,则将会设置计时器并中止信道。
  4. 如果调用方侦听的是 OperationContractAttribute.IsTerminating 操作的 OperationContext.OperationCompleted 事件,则在收到响应时可能会阻塞。 处理这种情况的正确方法是,当引发 OperationCompleted 时在其他线程上调度工作,然后从该事件处理程序立即返回。
  • ProtectionLevel 获取或设置一个值,该值指定是否必须对操作的消息进行加密和/或签名。
  1. 运行时的保护行为是在下列属性中设置的保护级别值的组合,这一点很重要。 这些属性具有层次结构。 除非已为较窄范围显式设置了某个不同的值,否则设置最外层的值将为所有较窄的范围确定默认设置。 在这种情况下,外层的值将保持所有较窄的范围的默认设置,但特定的设置除外。例如,如果将 ServiceContractAttribute.ProtectionLevel 设置为 ProtectionLevel.EncryptAndSign,并且其他较窄范围都没有设置保护级别,则会对操作协定中的所有消息进行加密和签名。 但是,如果其中一个操作将 ProtectionLevel 设置为 ProtectionLevel.Sign,那么对此操作的消息只进行签名,而对协定中的所有其他消息进行加密和签名。
  2. 当协定上没有显式指定保护级别并且基础绑定支持安全性时(无论处于传输级别还是处于消息级别),整个协定的有效保护级别将为 ProtectionLevel.EncryptAndSign。 如果绑定不支持安全性(如 BasicHttpBinding),则整个协定的有效 System.Net.Security.ProtectionLevel 为 ProtectionLevel.None。 因此,根据终结点绑定,即使协定指定了 ProtectionLevel.None,客户端也可以要求不同的消息或传输级别安全保护。
  3. 消息保护级别的层级结构:

  

  • ReplyAction:获取或设置用于该操作答复消息的 SOAP 操作的值。
  1. 除指定答复消息操作标头的特定值以外,还可以指定字符串“*”(星号)。 在服务中指定星号可指示 WCF 不向消息中添加答复操作,如果您是直接对消息进行编程会十分有用。 在客户端应用程序中指定星号可指示 WCF 不验证答复操作。 

WCF操作协定示例:

  • 解决方案如下:

  

  • 工程结构说明:
  1. Service:服务契约定义和实现。在服务契约接口中,我们定义了MethodOne、MethodTwo、MethodThree三个操作契约,其中MethodThree使用异步实现,这个示例也说明了异步服务的实现。在服务契约中,我们启用会话要求,将SessionMode设置为Required,目的是为了验证IsInitiating(启动服务器会话)和IsTerminating(服务操作在发送答复消息后,关闭会话)。

    设置了操作契约Action(设置请求消息的 WS-Addressing 操作)和ReplyAction(设置用于该操作答复消息的 SOAP 操作的值)

    ISampleMethod.cs的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Service
{
    [ServiceContract(Name = "SampleMethodContract",
        Namespace = "http://wangweimutou.SampleMethodContract",
        SessionMode=SessionMode.Required)]
    public interface ISampleMethod
    {
        /// <summary>
        /// IsInitiating默认值为true,IsTerminating默认值为false
        /// </summary>
        /// <param name="msg"></param>
        [OperationContract(Name="OCMethodOne",
            AsyncPattern=false,
            IsInitiating=true,
            IsTerminating=false,
            Action = "http://wangweimutou.SampleMethodContract/RequestMethodOne",
            ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodOne")]
        string MethodOne(string msg);

       [OperationContract(Name = "OCMethodTwo",
            AsyncPattern = false,
            IsInitiating = true,
            IsTerminating = false,
            Action = "http://wangweimutou.SampleMethodContract/RequestMethodTwo",
            ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodTwo")]
        string MethodTwo(string msg);

         [OperationContract(Name = "OCMethodThree",
            AsyncPattern = true,
            IsInitiating = true,
            IsTerminating = false,
            Action = "http://wangweimutou.SampleMethodContract/RequestMethodThree",
            ReplyAction = "http://wangweimutou.SampleMethodContract/ResponseMethodThree")]
          IAsyncResult BeginMethodThree(string msg, AsyncCallback callback, object asyncState);
          string EndMethodThree(IAsyncResult result);
    }
}

  SampleMethod.cs的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ServiceModel;

namespace Service
{
    public class SampleMethod :ISampleMethod
    {
        public string MethodOne(string msg)
        {
            return "You called MethodOne return message is: " + msg;
        }

        public string MethodTwo(string msg)
        {
            return "You called MethodTwo return message is: " + msg;
        }

        public IAsyncResult BeginMethodThree(string msg, AsyncCallback callback, object asyncState)
        {
            return new CompletedAsyncResult<string>(msg);
        }

        public string EndMethodThree(IAsyncResult r)
        {
            CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
            return "You called MethodThree return message is: " + result.Data;
        }
    }

    class CompletedAsyncResult<T> : IAsyncResult
    {
        T data;

        public CompletedAsyncResult(T data)
        { this.data = data; }

        public T Data
        { get { return data; } }

        #region IAsyncResult Members
        public object AsyncState
        { get { return (object)data; } }

        public WaitHandle AsyncWaitHandle
        { get { throw new Exception("The method or operation is not implemented."); } }

        public bool CompletedSynchronously
        { get { return true; } }

        public bool IsCompleted
        { get { return true; } }
        #endregion
    }
}

  2.  Host:控制台应用程序,服务承载程序。添加对Service程序集的引用,实现以下代码。Program.cs的代码如下: 

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Service;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(SampleMethod)))
            {
                host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); };
                host.Open();
                Console.Read();
            }
        }
    }
}

    App.config代码如下:

  

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>

        <services>
            <service name="Service.SampleMethod" behaviorConfiguration="mexBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:1234/SampleMethod/"/>
                    </baseAddresses>
                </host>
                <endpoint address="" binding="wsHttpBinding" contract="Service.ISampleMethod" />
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            </service>
        </services>

        <behaviors>
            <serviceBehaviors>
                <behavior name="mexBehavior">
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

  3.  Client:控制台应用程序。客户端应用程序,启动Host服务承载程序,添加对服务地址http://localhost:1234/SampleMethod/引用后,将命名空间设置为ServiceRef,

     勾选生成异步操作复选框,生成异步客户端代理类(参照WCF初探-11WCF客户端异步调用服务,我们就可以对程序进行同步和异步的调用了。Program.cs代码如下:

    

  运行程序,结果显示如下:

  

  从结果中,我们可以看到,程序停止了对MethodTwo和MethodThree的调用,这是因为MethodOne的IsTerminating设置为true,所以客户端代理在调用完MethodOne

  后就关闭了对服务器会话的支持。接下来,我们将调用MethodOne的代码注释掉,编译Client后再次运行,会得到一下结果:

  

  由于我们将MethodTwo的IsInitiating设置为了false,导致服务器会话没有启动,所以服务调用失败。接下来,我们将MethodOne、MethodTwo、MethodThree三个操作

  契约的IsInitiating和IsTerminating分别设置为true和false,也就是设置为默认值,重新编译程序后,运行客户端我们可以看到如下结果:

  

  接下来,我们再来查看一下操作契约设置的属性值,从上面的客户端程序代码可以看出,服务契约和操作契约的方法和名称都改成了设定值,如MethodOne变成了OCMethodOne。

  打开客户端测试程序,添加对服务地址的引用后,我们就可以看到消息的请求和响应,如观察到MethodOne调用的结果如下:

  

总结:

  • 在上面的示例中,我们修改了操作契约的部分属性,也从运行结果和交换的消息中验证了这些修改的属性。并且还完成了异步服务的实现。关于消息保护级别属性将在以后的博文中做解析。
  • 关于OperationContract的IsOneWay可以查看以下博文:

WCF初探-3:WCF消息交换模式之单向模式

WCF初探-5:WCF消息交换模式之双工通讯(Duplex)

WCF初探-13:WCF客户端为双工服务创建回调对象

  • 关于客户端异步调用服务可以查看以下博文:

   WCF初探-11:WCF客户端异步调用服务

时间: 2024-10-19 04:13:46

WCF初探-15:WCF操作协定的相关文章

WCF初探-26:WCF中的会话

理解WCF中的会话机制 在WCF应用程序中,会话将一组消息相互关联,从而形成对话.会话”是在两个终结点之间发送的所有消息的一种相互关系.当某个服务协定指定它需要会话时,该协定会指定所有调用(即,支持调用的基础消息交换)必须是同一对话的一部分.如果某个协定指定它允许使用会话但不要求使用会话,则客户端可以进行连接,并选择建立会话或不建立会话.如果会话结束,然后在同一个通道上发送消息,将会引发异常. WCF中的会话机制通过设置服务协定(ServiceContract)上的SessionMode的枚举值

WCF初探-20:WCF错误协定

WCF错误协定概述 在所有托管应用程序中,处理错误由 Exception 对象表示. 在基于 SOAP 的应用程序(如 WCF 应用程序)中,服务方法使用 SOAP 错误消息来传递处理错误信息. SOAP 错误是包括在服务操作元数据中的消息类型,因此会创建一个错误协定,客户端可使用该协定来使操作更加可靠或更具交互性. 此外,由于 SOAP 错误在客户端以 XML 格式表示,这是一种任何 SOAP 平台上的客户端都可以使用的具有极好的互操作性的类型系统,可增加 WCF 应用程序的适用范围. 由于

WCF初探-14:WCF服务协定

前言: 在前面的文章中,我们定义的服务协定上都会有一个ServiceContract的特性来修饰,这是因为服务契约的实现要靠ServiceContractAttribute 属性定义,然后使用一个或多个类(或接口)方法中的 OperationContractAttribute 属性定义协定的服务操作. 实现服务协定后并将其与WCF 绑定和 EndpointAddress 对象一起使用时,此服务协定将公开以供客户端使用. 公开的信息由 ServiceContractAttribute 表示,其接口

WCF初探-17:WCF数据协定之等效性

数据协定等效性特点概述 对于客户端要将某种类型的数据成功发送到服务,或者服务要将数据成功发送到客户端的情况,接收端上并不一定必须存在此发送数据类型. 唯一的要求是两种类型的数据协定应该等效. 要使数据协定等效,其命名空间和名称必须相同. 此外,某一端上的每个数据成员还必须在另一端上具有等效的数据成员.注意,数据协定名称和命名空间以及数据成员名称均区分大小写. 要使数据成员等效,其名称必须相同. 此外,它们还必须表示同一类型的数据,也就是说,其数据协定必须等效. 如果同一端(发送方或接收方)存在两

WCF初探-18:WCF数据协定之KnownType

KnownTypeAttribute 类概述 在数据到达接收终结点时,WCF 运行库尝试将数据反序列化为公共语言运行库 (CLR) 类型的实例.通过首先检查传入消息选择为反序列化而实例化的类型,以确定消息内容遵循的数据协定.然后反序列化引擎尝试查找实现与消息内容兼容的数据协定的 CLR 类型.反序列化引擎在此过程中允许的侯选类型集称为反序列化程序的“已知类型”集. 让反序列化引擎了解某个类型的一种方法是使用 KnownTypeAttribute.不能将属性应用于单个数据成员,只能将它应用于整个数

WCF初探-22:WCF中使用Message类(上)

前言 从我们学习WCF以来,就一直强调WCF是基于消息的通信机制.但是由于WCF给我们做了高级封装,以至于我们在使用WCF的时候很少了解到消息的内部机制.由于WCF的架构的可扩展性,针对一些特殊情况,WCF为我们提供了Message类来深度定制消息结构,以便我们拓展WCF的通信机制. 在之前的文章中,我们针对一些常用的WCF传递数据的方式进行了说明,比如数据协定和消息协定等.他们传递的数据最终都会转化为消息的实例.具体参照:        WCF初探-16:WCF数据协定之基础知识       

WCF初探-13:WCF客户端为双工服务创建回调对象

前言: 在WCF初探-5:WCF消息交换模式之双工通讯(Duplex)博文中,我讲解了双工通信服务的一个应用场景,即订阅和发布模式,这一篇,我将通过一个消息发送的例子讲解一下WCF客户端如何为双工服务创建回调对象. 双工服务指定一个回调协定,客户端应用程序必须实现该协定以便提供一个该服务能够根据协定要求调用的回调对象.虽然回调对象不是完整的服务(例如,您无法使用回调对象启动一个通道),但是为了实现和配置,这些回调对象可以被视为一种服务. 双工服务的客户端必须: 实现一个回调协定类. 创建回调协定

WCF初探-10:WCF客户端调用服务

创建WCF 服务客户端应用程序需要执行下列步骤: 获取服务终结点的服务协定.绑定以及地址信息 使用该信息创建 WCF 客户端 调用操作 关闭该 WCF 客户端对象 WCF客户端调用服务存在以下特点: 服务和客户端使用托管属性.接口和方法对协定进行建模. 若要连接客户端应用程序中的服务,则需要获取该服务协定的类型信息.通常,我们使用Svcutil.exe(ServiceModel Metadata Utility Tool)来完成,也可以直接在客户端项目上引用服务地址完成.它们会从服务中下载元数据

菜鸟眼中的WCF(2)—服务协定

在上一篇文章中简单的对WCF进行了讲解并做了一个简单的实例,那么今天就继续对它进行解析--服务协定的初探. 说到协定大家肯定不陌生,只要参加了工作就一定会签订各种协定,比如合同的签订,我们就是其中的一方,按着这个双方签订的合同来工作,它授予你什么权限,你就有什么权限,不能做本职外的工作.各位想一想这个合同的协议,像不像我们这里说的服务协定?是啊,服务协定其实没啥了不起的,刚才签下合作协议的个人和公司分别叫做"客户端"和"服务器". 服务协定告诉客户端,哪些方法你可以