基于事件的异步编程和Web服务

来源:互联网 发布:快乐码字软件 编辑:程序博客网 时间:2024/04/25 06:47
       对于基于事件的异步编程请看MSDN帮助http://msdn2.microsoft.com/zh-cn/library/wewwczdw(VS.80).aspx。里边有详细的介绍。偶然,我最近看设计模式的代理模式,C#中代理模式最经典的应用就是Web Service了,个人随便建立了个例子。
 1using System;
 2using System.Web;
 3using System.Web.Services;
 4using System.Web.Services.Protocols;
 5
 6[WebService(Namespace = "http://tempuri.org/")]
 7[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
 8public class Service : System.Web.Services.WebService
 9{
10    public Service () {
12        //Uncomment the following line if using designed components 
13        //InitializeComponent(); 
14    }

15
16    [WebMethod]
17    public string HelloWorld() {
18        return "Hello World";
19    }

20
21    [WebMethod]
22    public int GetInt()
23    {
24        return 1;
25    }

26}

这个类应该是运行在另外一台机器上的,一般用做数据库的操作等方面。
在客户端,我们可以用WSDL工具或微软的IDE给一个Project添加Web引用,不给出具体的操作。
添加好之后,我们看看生成的本地代理类。
  1//------------------------------------------------------------------------------
  2// <auto-generated>
  3//     This code was generated by a tool.
  4//     Runtime Version:2.0.50727.42
  5//
  6//     Changes to this file may cause incorrect behavior and will be lost if
  7//     the code is regenerated.
  8// </auto-generated>
  9//------------------------------------------------------------------------------
 10
 11// 
 12// This source code was auto-generated by Microsoft.VSDesigner, Version 2.0.50727.42.
 13// 
 14#pragma warning disable 1591
 15
 16namespace CourtMediaUI.TestService {
 17    using System.Diagnostics;
 18    using System.Web.Services;
 19    using System.ComponentModel;
 20    using System.Web.Services.Protocols;
 21    using System;
 22    using System.Xml.Serialization;
 23    
 24    
 25    /// <remarks/>
 26    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services""2.0.50727.42")]
 27    [System.Diagnostics.DebuggerStepThroughAttribute()]
 28    [System.ComponentModel.DesignerCategoryAttribute("code")]
 29    [System.Web.Services.WebServiceBindingAttribute(Name="ServiceSoap", Namespace="http://tempuri.org/")]
 30    public partial class Service : System.Web.Services.Protocols.SoapHttpClientProtocol {
 31        
 32        private System.Threading.SendOrPostCallback HelloWorldOperationCompleted;
 33        
 34        private System.Threading.SendOrPostCallback GetIntOperationCompleted;
 35        
 36        private bool useDefaultCredentialsSetExplicitly;
 37        
 38        /// <remarks/>
 39        public Service() {
 40            this.Url = global::CourtMediaUI.Properties.Settings.Default.CourtMediaUI_TestService_Service;
 41            if ((this.IsLocalFileSystemWebService(this.Url) == true)) {
 42                this.UseDefaultCredentials = true;
 43                this.useDefaultCredentialsSetExplicitly = false;
 44            }

 45            else {
 46                this.useDefaultCredentialsSetExplicitly = true;
 47            }

 48        }

 49        
 50        public new string Url {
 51            get {
 52                return base.Url;
 53            }

 54            set {
 55                if ((((this.IsLocalFileSystemWebService(base.Url) == true
 56                            && (this.useDefaultCredentialsSetExplicitly == false)) 
 57                            && (this.IsLocalFileSystemWebService(value) == false))) {
 58                    base.UseDefaultCredentials = false;
 59                }

 60                base.Url = value;
 61            }

 62        }

 63        
 64        public new bool UseDefaultCredentials {
 65            get {
 66                return base.UseDefaultCredentials;
 67            }

 68            set {
 69                base.UseDefaultCredentials = value;
 70                this.useDefaultCredentialsSetExplicitly = true;
 71            }

 72        }

 73        
 74        /// <remarks/>
 75        public event HelloWorldCompletedEventHandler HelloWorldCompleted;
 76        
 77        /// <remarks/>
 78        public event GetIntCompletedEventHandler GetIntCompleted;
 79        
 80        /// <remarks/>
 81        [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/HelloWorld", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
 82        public string HelloWorld() {
 83            object[] results = this.Invoke("HelloWorld"new object[0]);
 84            return ((string)(results[0]));
 85        }

 86        
 87        /// <remarks/>
 88        public void HelloWorldAsync() {
 89            this.HelloWorldAsync(null);
 90        }

 91        
 92        /// <remarks/>
 93        public void HelloWorldAsync(object userState) {
 94            if ((this.HelloWorldOperationCompleted == null)) {
 95                this.HelloWorldOperationCompleted = new System.Threading.SendOrPostCallback(this.OnHelloWorldOperationCompleted);
 96            }

 97            this.InvokeAsync("HelloWorld"new object[0], this.HelloWorldOperationCompleted, userState);
 98        }

 99        
100        private void OnHelloWorldOperationCompleted(object arg) {
101            if ((this.HelloWorldCompleted != null)) {
102                System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
103                this.HelloWorldCompleted(thisnew HelloWorldCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
104            }

105        }

106        
107        /// <remarks/>
108        [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/GetInt", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
109        public int GetInt() {
110            object[] results = this.Invoke("GetInt"new object[0]);
111            return ((int)(results[0]));
112        }

113        
114        /// <remarks/>
115        public void GetIntAsync() {
116            this.GetIntAsync(null);
117        }

118        
119        /// <remarks/>
120        public void GetIntAsync(object userState) {
121            if ((this.GetIntOperationCompleted == null)) {
122                this.GetIntOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetIntOperationCompleted);
123            }

124            this.InvokeAsync("GetInt"new object[0], this.GetIntOperationCompleted, userState);
125        }

126        
127        private void OnGetIntOperationCompleted(object arg) {
128            if ((this.GetIntCompleted != null)) {
129                System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
130                this.GetIntCompleted(thisnew GetIntCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
131            }

132        }

133        
134        /// <remarks/>
135        public new void CancelAsync(object userState) {
136            base.CancelAsync(userState);
137        }

138        
139        private bool IsLocalFileSystemWebService(string url) {
140            if (((url == null
141                        || (url == string.Empty))) {
142                return false;
143            }

144            System.Uri wsUri = new System.Uri(url);
145            if (((wsUri.Port >= 1024
146                        && (string.Compare(wsUri.Host, "localHost", System.StringComparison.OrdinalIgnoreCase) == 0))) {
147                return true;
148            }

149            return false;
150        }

151    }

152    
153    /// <remarks/>
154    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services""2.0.50727.42")]
155    public delegate void HelloWorldCompletedEventHandler(object sender, HelloWorldCompletedEventArgs e);
156    
157    /// <remarks/>
158    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services""2.0.50727.42")]
159    [System.Diagnostics.DebuggerStepThroughAttribute()]
160    [System.ComponentModel.DesignerCategoryAttribute("code")]
161    public partial class HelloWorldCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {
162        
163        private object[] results;
164        
165        internal HelloWorldCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : 
166                base(exception, cancelled, userState) {
167            this.results = results;
168        }

169        
170        /// <remarks/>
171        public string Result {
172            get {
173                this.RaiseExceptionIfNecessary();
174                return ((string)(this.results[0]));
175            }

176        }

177    }

178    
179    /// <remarks/>
180    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services""2.0.50727.42")]
181    public delegate void GetIntCompletedEventHandler(object sender, GetIntCompletedEventArgs e);
182    
183    /// <remarks/>
184    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services""2.0.50727.42")]
185    [System.Diagnostics.DebuggerStepThroughAttribute()]
186    [System.ComponentModel.DesignerCategoryAttribute("code")]
187    public partial class GetIntCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {
188        
189        private object[] results;
190        
191        internal GetIntCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : 
192                base(exception, cancelled, userState) {
193            this.results = results;
194        }

195        
196        /// <remarks/>
197        public int Result {
198            get {
199                this.RaiseExceptionIfNecessary();
200                return ((int)(this.results[0]));
201            }

202        }

203    }

204}

205
206#pragma warning restore 1591


我们知道Web服务主要是通过Http的Soap协议发送和接收的。我们看此代理类的基类是SoapHttpClientProtocol,也就是说此代理类封装了很多协议的发送和接收,我们使用起来非常方便,不用管底层的协议的处理,这也正是代理模式的优点:对类的控制容易性。
另外一方面,此代理类怎么和基于事件的异步这么相似呢?
        (1)基于事件的异步模式可以采用多种形式,具体取决于某个特定类支持的操作的复杂程度。最简单的类可能只有一个 MethodNameAsync 方法和一个对应的 MethodNameCompleted 事件。更复杂的类可能有若干个 MethodNameAsync 方法(每种方法都有一个对应的 MethodNameCompleted 事件),以及这些方法的同步版本。这些类分别支持各种异步方法的取消、进度报告和增量结果。
        异步方法可能还支持多个挂起的调用(多个并行调用),A:允许您的代码在此方法完成其他挂起的操作之前调用此方法任意多次。若要正确处理此种情况,必须让您的应用程序能够跟踪各个操作的完成。

而此代理类每个方法,比如HelloWorld同步方法,也有相对应的HelloWorldAsync()和HelloWorldAsync(object userState)异步方法。但要注意的是这2个异步方法的返回值是void,这是因为这些方法是基于事件的,标注的红色A也正说明了这点。

下边简述下基于事件的异步模式:

1。概述

基于事件的异步模式具有多线程应用程序的优点,同时隐匿了多线程设计中固有的许多复杂问题。使用支持此模式的类,您将能够:

  • “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。

  • 同时执行多个操作,每个操作完成时都会接到通知。

  • 等待资源变得可用,但不会停止(“挂起”)您的应用程序。

  • 使用熟悉的事件和委托模型与挂起的异步操作通信。有关使用事件处理程序和委托的更多信息,请参见事件和委托

支持基于事件的异步模式的类将有一个或多个名为 MethodNameAsync 的方法。这些方法可能会创建同步版本的镜像,这些同步版本会在当前线程上执行相同的操作。此类还可能有一个 MethodNameCompleted 事件,而且它可能会有一个 MethodNameAsyncCancel(或只是 CancelAsync)方法。

2。基于事件的异步模式的特征

基于事件的异步模式可以采用多种形式,具体取决于某个特定类支持的操作的复杂程度。最简单的类可能只有一个 MethodNameAsync 方法和一个对应的 MethodNameCompleted 事件。更复杂的类可能有若干个 MethodNameAsync 方法(每种方法都有一个对应的 MethodNameCompleted 事件),以及这些方法的同步版本。这些类分别支持各种异步方法的取消、进度报告和增量结果。

异步方法可能还支持多个挂起的调用(多个并行调用),允许您的代码在此方法完成其他挂起的操作之前调用此方法任意多次。若要正确处理此种情况,必须让您的应用程序能够跟踪各个操作的完成。

3。异步方法重载

异步操作可以有两个重载:单调用和多调用。您可以通过方法签名来区分这两种形式:多调用形式有一个额外的参数,即 userState。使用这种形式,您的代码可以多次调用 Method1Async(string param, object userState),而不必等待任何挂起的异步操作的完成。另一方面,如果您尝试在前一个调用尚未完成时调用 Method1Async(string param),该方法将引发 InvalidOperationException

多调用重载的 userState 参数可帮助您区分各个异步操作。您应分别为各个 Method1Async(string param, object userState) 调用提供一个唯一值(例如 GUID 或哈希代码);这样,当各个操作完成时,您的事件处理程序便可以确定哪个操作的实例引发了完成事件。

4。跟踪挂起的操作

如果您使用多调用重载,您的代码将需要跟踪挂起的任务的 userState 对象(任务 ID)。对于每个 Method1Async(string param, object userState) 调用,您通常应生成一个新的、唯一的 userState 对象并将此对象添加到集合中。当对应于此 userState 对象的任务引发完成事件时,您的完成方法实现将检查 System.ComponentModel.AsyncCompletedEventArgs.UserState 并将此对象从集合中删除。在以这种方式使用时,userState 参数充当任务 ID 的角色。

Note注意

在为您对多调用重载的调用中的 userState 提供唯一值时,一定要小心。如果任务 ID 不唯一,将导致异步类引发 ArgumentException

5。取消挂起的操作

我们必须能够在异步操作完成之前随时取消它们,这一点很重要。实现基于事件的异步模式的类将有一个 CancelAsync 方法(如果有多个异步方法)或 MethodNameAsyncCancel 方法(如果只有一个异步方法)。

允许多个调用的方法采用 userState 参数,此参数可用来跟踪各个任务的生存期。CancelAsync 采用 userState 参数,此参数允许您取消特定的挂起任务。

一次只支持一个挂起的操作的方法(如 Method1Async(string param))是不可取消的。

6。接收进度更新和增量结果

符合基于事件的异步模式的类可以为跟踪进度和增量结果提供事件。此事件通常叫做 ProgressChangedMethodNameProgressChanged,它对应的事件处理程序会带有一个 ProgressChangedEventArgs 参数。

ProgressChanged 事件的事件处理程序可以检查 System.ComponentModel.ProgressChangedEventArgs.ProgressPercentage 属性来确定异步任务完成的百分比。此属性的范围是 0 到 100,可用来更新 ProgressBarValue 属性。如果有多个异步操作挂起,您可以使用 System.ComponentModel.ProgressChangedEventArgs.UserState 属性来分辨出哪个操作在报告进度。

一些类可能会在异步操作继续时报告增量结果。这些结果将保存的派生自 ProgressChangedEventArgs 的类中,并显示为此派生类中的属性。您可以在 ProgressChanged 事件的事件处理程序中访问这些结果,就像访问 ProgressPercentage 属性一样。如果有多个异步操作挂起,您可以使用 UserState 属性来分辨出哪个操作在报告增量结果。