[原创]WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
说完了客户端的异步服务调用(参阅WCF技术剖析之十一:异步操作在WCF中的应用(上篇)),我们在来谈谈服务端如何通过异步的方式为服务提供实现。在定义服务契约的时候,相信大家已经注意到了OperationContractAttribute特性具有一个bool类型的AsynPattern。该属性可以将一个服务操作定义成异步实现模式,接下来的内容主要是着眼于介绍异步操作的定义和实现原理。一、异步操作的定义和实现原理
实现WCF异步服务操作模式在编程上具有一些限制:异步服务操作是通过两个配对的方法实现的,并且采用典型的异步操作命名方式:BeginXxx/EndXxx。两个方法需要采用如下的签名,指定了AsyncPattern属性的OperationContractAttribute只需要应用到BeginXxx方法上面。
1: 2: IAsyncResult BeginDoWork(parameters, AsyncCallback userCallback, object stateObject); 3: ReturnType EndDoWork(IAsyncResult asynResult);比如下面两段代码可以看作相同的操作在同步和异步下的不同表现。
1: 2: double Add(double x, double y); 1: 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: 6: public interface IFileReader 7: { 8: 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 Members14: 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;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: #endregion30: }31: }采用传统的方式寄宿该服务,并发布元数据。在客户端通过添加服务引用的方式生成相关的服务代理代码和配置。你将会发现客户端生成的服务契约和服务代理类中,会有一个唯一的操作Read。也就是说,不管服务采用同步模式还是异步模式实现,对客户端的服务调用方式没有任何影响,客户端可以任意选择相应的模式进行服务调用。
1: namespace Clients.ServiceReferences 2: { 3: 4: public interface IFileReader 5: { 6: 7: string Read(string fileName); 8: } 9: 10: public partial class FileReaderClient : ClientBase<IFileReader>, IFileReader11: {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: }
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]