閱讀719 返回首頁    go 技術社區[雲棲]


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="https://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 = " https://www.artech.com/IFileReader/Read", ReplyAction = " https://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: }

 


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 14:03:49

  上一篇:go  WCF技術剖析之十一:異步操作在WCF中的應用(上篇)
  下一篇:go  WCF技術剖析之十二:數據契約(Data Contract)和數據契約序列化器(DataContractSerializer)