zoukankan      html  css  js  c++  java
  • WCF技术剖析之十一:异步操作在WCF中的应用(下篇)

    说完了客户端的异步服务调用(参阅WCF技术剖析之十一:异步操作在WCF中的应用(上篇)),我们在来谈谈服务端如何通过异步的方式为服务提供实现。在定义服务契约的时候,相信大家已经注意到了OperationContractAttribute特性具有一个bool类型的AsynPattern。该属性可以将一个服务操作定义成异步实现模式,接下来的内容主要是着眼于介绍异步操作的定义和实现原理。

    一、异步操作的定义和实现原理

    实现WCF异步服务操作模式在编程上具有一些限制:异步服务操作是通过两个配对的方法实现的,并且采用典型的异步操作命名方式:BeginXxx/EndXxx。两个方法需要采用如下的签名,指定了AsyncPattern属性的OperationContractAttribute只需要应用到BeginXxx方法上面。

       1: [OperationContract(AsyncPattern = true)]
       2: IAsyncResult BeginDoWork(parameters, AsyncCallback userCallback, object stateObject);
       3: ReturnType   EndDoWork(IAsyncResult asynResult);

    比如下面两段代码可以看作相同的操作在同步和异步下的不同表现。

       1: [OperationContract]
       2: double Add(double x, double y);
       1: [OperationContract(AsyncPattern = true)]
       2: IAsyncResult BeginAdd(double x, double y, AsyncCallback userCallback, object stateObject);
       3: double EndAdd(IAsyncResult asynResult);

    理解了异步操作的定义模式之后,我们来谈谈WCF异步操作实现的原理。WCF通过类型OperationDescription表示对服务操作的描述。如下面的代码所示,OperationDescription具有3个重要的MemthodInfo类型的属性成员:SyncMethod、BeginMethod和EndMethod,分别表示同步方法、异步开始和结束方法。以上面的代码为例,如果采用SyncMethod表示Add方法,而BeginMethod和EndMethod对应于BeginAdd和EndAdd方法。

       1: public class OperationDescription
       2: {  
       3:     
       4:     public MethodInfo SyncMethod { get; set; }
       5:     public MethodInfo BeginMethod { get; set; }
       6:     public MethodInfo EndMethod { get; set; }
       7:     //其他成员  
       8: }

    WCF通过OperationSelector选择相应的操作,通过OperationInvoker执行被选择操作对应的方法。所有的OperationInvoker都实现了接口System.ServiceModel.Dispatcher.IOperationInvoker。下面是IOperationInvoker基本的定义。Invoke和InvokeBegin/InvokeEnd代表对操作同步和异步执行,IsSynchronous表示当前操作是否是异步的,如果操作的AsyncPattern为true则表明是异步操作。

       1: public interface IOperationInvoker
       2: {    
       3:     object[] AllocateInputs();
       4:     object Invoke(object instance, object[] inputs, out object[] outputs);
       5:     IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state);
       6:     object InvokeEnd(object instance, out object[] outputs, IAsyncResult result);   
       7:     bool IsSynchronous { get; }
       8: }

    在WCF中定义了两个典型的OperationInvoker:SyncOperationInvoker与AsyncOperationInvoker,它们分别用于同步操作和异步操作的执行。这两个OperationINvoker均实现了IOperationInvoker接口,SyncOperationInvoker实现了Invoke方法,AsyncOperationInvoker实现了InvokeBegin和InvokeEnd

    当通过OperationSelector和InstanceProvider选出正确的方法和得到相应的服务实例的时候,WCF根据操作的AsyncPattern选择相应的OperationInvoker。如果是同步的则自然选择SyncOperationInvoker,执行Invoke方法。Invoke方法会通过OperationDescription的SyncMethod属性,得到同步操作方法的MethodInfo,采用反射的机制执行该方法;对于异步操作,则会调用AsyncOperationInvoker的InvokeBegin和InvokeEnd方法,InvokeBegin和InvokeEnd方法对应的MethodInfo通过OperationDescription的BeginMethod和EndMethod属性获得。得到相应的MethodInfo对象后,同样通过反射调用服务实例。

    二、如何创建异步服务

    在了解了异步操作的定义和具体的实现原理之后,我们通过一个简单的实例演示异步操作在WCF应用中的实现。本例子中,我们通过服务调用来读取服务端的文件,在实现文件读取操作的时候,采用异步文件读取方式。

    先来看看服务契约的定义。服务契约通过接口IFileReader定义,基于文件名的文件读取操作以异步的方式定义在BeginRead和EndRead方法中。

       1: using System;
       2: using System.ServiceModel;
       3: namespace Artech.AsyncServices.Contracts
       4: {
       5:     [ServiceContract(Namespace="http://www.artech.com/")]
       6:     public interface IFileReader
       7:     {
       8:         [OperationContract(AsyncPattern = true)]
       9:         IAsyncResult BeginRead(string fileName, AsyncCallback userCallback, object stateObject);
      10:  
      11:         string EndRead(IAsyncResult asynResult);
      12:     }
      13: }

    FileReader实现了契约契约,在BeginRead方法中,根据文件名称创建FileStream对象,调用FileStream的BeginRead方法实现文件的异步读取,并直接返回该方法的执行结果:一个IAsyncResult对象。在EndRead方法中,调用FileStream的EndRead读取文件内容,并关闭FileStream对象。

       1: using System;
       2: using System.Text;
       3: using Artech.AsyncServices.Contracts;
       4: using System.IO;
       5: namespace Artech.AsyncServices.Services
       6: {
       7:     public class FileReaderService : IFileReader
       8:     {
       9:         private const string baseLocation = @"E:\";
      10:         private FileStream _stream;
      11:         private byte[] _buffer;
      12:  
      13:         #region IFileReader Members
      14:  
      15:         public IAsyncResult BeginRead(string fileName, AsyncCallback userCallback, object stateObject)
      16:         {
      17:             this._stream = new FileStream(baseLocation + fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
      18:             this._buffer = new byte[this._stream.Length];
      19:             return this._stream.BeginRead(this._buffer, 0, this._buffer.Length, userCallback, stateObject);
      20:         }
      21:  
      22:         public string EndRead(IAsyncResult ar)
      23:         {
      24:             this._stream.EndRead(ar);
      25:             this._stream.Close();
      26:             return Encoding.ASCII.GetString(this._buffer);
      27:         }
      28:  
      29:         #endregion
      30:     }
      31: }

    采用传统的方式寄宿该服务,并发布元数据。在客户端通过添加服务引用的方式生成相关的服务代理代码和配置。你将会发现客户端生成的服务契约和服务代理类中,会有一个唯一的操作Read。也就是说,不管服务采用同步模式还是异步模式实现,对客户端的服务调用方式没有任何影响,客户端可以任意选择相应的模式进行服务调用。

       1: namespace Clients.ServiceReferences
       2: {
       3:     [ServiceContractAttribute(ConfigurationName= "ServiceReferences.IFileReader")]
       4:     public interface IFileReader
       5:     {
       6:         [OperationContractAttribute(Action = " http://www.artech.com/IFileReader/Read", ReplyAction = " http://www.artech.com/IFileReader/ReadResponse")]
       7:         string Read(string fileName);
       8:     }
       9:  
      10:     public partial class FileReaderClient : ClientBase<IFileReader>, IFileReader
      11:     {
      12:  
      13:         public string Read(string fileName)
      14:         {
      15:             return base.Channel.Read(fileName);
      16:         }
      17:     }
      18: }

    直接借助于生成的服务代理类FileReaderClient,服务调用的代码就显得很简单了。

       1: using System;
       2: using Clients.ServiceReferences;
       3: namespace Clients
       4: {
       5:     class Program
       6:     {
       7:         static void Main(string[] args)
       8:         {
       9:             using (FileReaderClient proxy = new FileReaderClient())
      10:             { 
      11:                 Console.WriteLine(proxy.Read("test.txt"));
      12:             }
      13:             Console.Read();
      14:         }
      15:     }
      16: }
    作者:Artech
    出处:http://artech.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Binary Search Tree Iterator 解答
    Invert Binary Tree 解答
    Min Stack 解答
    Trapping Raining Water 解答
    Candy 解答
    Jump Game II 解答
    Implement Hash Map Using Primitive Types
    Gas Station 解答
    Bucket Sort
    HashMap 专题
  • 原文地址:https://www.cnblogs.com/artech/p/1519499.html
Copyright © 2011-2022 走看看