博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
阅读量:7145 次
发布时间:2019-06-29

本文共 5800 字,大约阅读时间需要 19 分钟。

说完了客户端的异步服务调用(参阅),我们在来谈谈服务端如何通过异步的方式为服务提供实现。在定义服务契约的时候,相信大家已经注意到了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
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
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
又一道简单题&&Ladygod(两道思维水题)
查看>>
golang笔记——函数与方法
查看>>
Linux LVM硬盘管理及LVM扩容
查看>>
针对某个数据库error做systemstate dump
查看>>
iOS开发--SWRevealViewController
查看>>
JSP--百度百科
查看>>
TCP/IP详解学习笔记(2)-数据链路层
查看>>
VMware+Windgb+Win7内核驱动调试
查看>>
initWithFrame、initWithCoder、awakeFromNib的区别和调用次序 & UIViewController生命周期 查缺补漏...
查看>>
客户端请求新页面
查看>>
VMware安装CentOS时,无法以图形界面安装解决办法
查看>>
SpringMvc文件资源防止被外链链接
查看>>
Spring 4 官方文档学习(十一)Web MVC 框架
查看>>
使用 Spring Boot 快速构建 Spring 框架应用--转
查看>>
Quartz 2D
查看>>
Eclipse 快捷键
查看>>
VC++ 设置软件开机自启动的方法
查看>>
MyBatis学习(三)、动态SQL语句
查看>>
PLSQL:[1]plsql中文乱码,显示问号
查看>>
将十进制转成十六进制
查看>>