NET应用程序中异步调用Web Service的几种方法
作者:veryhappy(wx.net),http://blog.csdn.net/veryhappy,转载请注明
摘要
想必做.NET下开发的人对调用Web Service并不陌生,本文不介绍Web Service如何编写,只介绍.NET 应用程序中如何异步调用Web Service的几种方法。示例采用Windows应用程序。异步调用很有用,可以让客户端调用Web Service时(一般耗时未知),不至于阻塞客户端的UI线程导致假死的样子,还可以在调用Web Service的同时做些其它的处理。希望能对大家有所帮助,借此提出更好的解决方法。
测试程序界面
图一,调用前界面
图二,调用后界面
详细代码实现
闲言少叙,直接进入主题吧。首先一个声明一个类(将来在客户端与服务器间传递):
{
private int _ID;
private string _Name;
public int ID
{
get
{
return _ID;
}
set
{
_ID = value;
}
}
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
}
其次,声明一个Web方法:
public class Service1 : System.Web.Services.WebService
{
//省略系统生成的Web Service声明代码,采用默认的
[WebMethod]
public Class1 GetClass1 ( int p_TimeSpan )
{
System.Threading.Thread.Sleep( p_TimeSpan ); // 线程停止一段执行,假设一段处理的时间
Class1 r = new Class1();
r.Name = "Async Call";
r.ID = p_TimeSpan;
return r;
}
}
客户端调用代码,设计一个Windows Form窗体来测试,放入3个按钮控件(分别代表3种异步调用方法),一个进度条控件(表示调用Web Service后,客户端继续进行某些操作)。先把3种方法介绍一下:
1. 利用Backgroundworker对象,所在命名空间System.ComponentModel。MSDN原文解释:BackgroundWorker 类允许您在单独的专用线程上运行操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 似乎处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用 BackgroundWorker 类方便地解决问题。
2. 利用wsdl.exe生成Web Service的客户端代理类,调用其中WebMethod的Async方法。
3. 利用wsdl.exe生成Web Service的客户端代理类,分别调用其中WebMethod的Begin与End方法。
先用wsdl.exe把上面的Web Service生成客户端代理类并把他编译成类库引入Windows应用项目(这里我没有编译类库,而是直接把代理类.cs文件加入到了Windows应用项目中)。
进入Visual Studio命令行模式,运行
wsdl /out:MyService.cs http://localhost:2732/Service1.asmx
参数说明:第二个参数是Web Service的URL,这个是我机器的本地环境,其他人需要根据情况修改!
注:第一种方式的调用,不采用手动生成Web Service客户端代理类也可以(项目中直接添加Web引用)。后面两种方法都是通过Web Service代理类中提供的方法实现,因此必须生成这个代理类。
看一下处理进度条控件值的代码,几种调用方法都会用到,
{
for ( int i = 0; i < 10; i++ )
{
progressBar1.Value = i;
// 不知道用多少时间,慢慢走吧:)
System.Threading.Thread.Sleep( 500 );
}
}
第一种方式调用,
{
AsyncCall_In_Backgroundworker();
}
#region AsyncCall_In_Backgroundworker
private void AsyncCall_In_Backgroundworker ( )
{
BackgroundWorker backgroundworker = new BackgroundWorker();
backgroundworker.DoWork += new DoWorkEventHandler( back_DoWork );// 注册具体异步处理的方法
backgroundworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( back_RunWorkerCompleted );// 注册调用完成后的回调方法
backgroundworker.RunWorkerAsync();// 这里开始异步调用
ChangeProcessBar(); // 调用同时客户端处理不停止
}
void back_RunWorkerCompleted ( object sender, RunWorkerCompletedEventArgs e )
{
if ( e.Error != null )
throw e.Error;
progressBar1.Value = progressBar1.Maximum; // 调用完成了,把客户端进度条填充满
Class1 result = e.Result as Class1; // 结果转化为Class1对象
MessageBox.Show( "ID is " + ( result.ID.ToString() + ",Name is " + result.Name.ToString() ) ); //显示从服务器获取的对象值
}
void back_DoWork ( object sender, DoWorkEventArgs e )
{
Service1 service1 = new Service1(); // Web Service代理类
e.Result = service1.GetClass1( 2500 ); // 调用Web方法GetClass1,结果赋值给DoWorkEventArgs的Result对象
}
#endregion
第二种方式调用,
{
AsynCall_In_WebServiceProxy();
}
#region AsynCall_In_WebServiceProxy
private void AsynCall_In_WebServiceProxy ( )
{
Service1 service1 = new Service1(); // Web Service代理类
service1.GetClass1Async( 3000 ); // 这里开始异步调用
service1.GetClass1Completed += new GetClass1CompletedEventHandler( s_GetClass1Completed ); // 注册调用完成后的回调方法
ChangeProcessBar();// 调用同时客户端处理不停止
}
void s_GetClass1Completed ( object sender, GetClass1CompletedEventArgs e )
{
if ( e.Error != null )
throw e.Error;
progressBar1.Value = progressBar1.Maximum; // 调用完成了,把客户端进度条填充满
Class1 result = e.Result as Class1; // 结果转化为Class1对象
MessageBox.Show( "ID is " + ( result.ID.ToString() + ",Name is " + result.Name.ToString() ) ); // 显示从服务器获取的对象值
}
#endregion
第三种调用,
{
AsynCall_In_WebServiceProxyOther();
}
#region AsynCall_In_WebServiceProxyOther
private void AsynCall_In_WebServiceProxyOther ( )
{
Service1 service1 = new Service1(); // Web Service代理类
service1.BeginGetClass1( 3000, OnWebMethodCompleted, null ); // 这里开始异步调用
ChangeProcessBar();// 调用同时客户端处理不停止
}
private void OnWebMethodCompleted ( IAsyncResult p_AsyncResult )
{ // 方法声明必须带有IAsyncResult类型对象
Service1 service1 = new Service1();// Web Service代理类
Class1 result = service1.EndGetClass1( p_AsyncResult ); // 结束调用时结果返回
MessageBox.Show( "ID is " + ( result.ID.ToString() + ",Name is " + result.Name.ToString() ) ); // 显示从服务器获取的对象值
}
#endregion
细心的读者可能发现第三种方法并没有在调用完成后把客户端的进度条填满,的确在OnWebMethodCompleted中调用progressBar1.Value = progressBar1.Maximum时会出现InvalidOperationException异常,线程间操作无效: 从不是创建控件“progressBar1”的线程访问它。这个异常恐怕在.net下写过多线程操作的人都看到过 :)。可以声明一个委托,把处理进度条的代码放到一个方法中,用窗体的Invoke去安全的调用它。好下面把修改的代码写出来,注意加粗的部分
private void OnWebMethodCompleted ( IAsyncResult p_AsyncResult )
{
Service1 service1 = new Service1();
Class1 result = service1.EndGetClass1( p_AsyncResult );
MessageBox.Show( "ID is " + ( result.ID.ToString() + ",Name is " + result.Name.ToString() ) );
this.Invoke( new MaxProcessBarEventHandle( MaxProcessBar ) ); // 从进度条创建的线程安全调用MaxProcessBar方法
}
private void MaxProcessBar ( )
{
// 客户端进度条最大化方法
progressBar1.Value = progressBar1.Maximum;
}