探索异步Web服务的奥秘

在现代Web开发中,异步Web服务的使用越来越普遍。它们允许客户端在等待服务器响应的同时继续执行其他任务,从而提高了用户体验和应用程序的性能。然而,实现异步Web服务并不总是一帆风顺的。本文将分享一段关于异步Web服务的探索之旅,包括遇到的挑战和解决方案。

简单的Web服务示例

从一个简单的C# Web服务开始。这个服务有一个方法,它会延迟10秒钟,然后返回一个字符串。这个延迟是为了更明显地展示异步调用的效果。

public class Service1 : System.Web.Services.WebService { [WebMethod] public string HelloWorld() { System.Threading.Thread.Sleep(10 * 1000); return "Hello World"; } }

为了测试这个服务,将其添加到一个网站中,并通过ASP.NET页面调用它。需要注意的是,调用异步方法的ASP.NET页面必须在页面的头部设置Async属性为true。

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Async="true" %>

接下来,创建一个Web服务代理,并连接事件处理程序。将代理对象作为Page类的成员,以便在不同事件之间保持实例化。

public partial class _Default : System.Web.UI.Page { localhost.Service1 MyService; protected void Page_Load(object sender, EventArgs e) { MyService = new localhost.Service1(); MyService.HelloWorldCompleted += EventHandler; } }

然后,编写代码来调用Web服务并处理事件。这里使用了一个辅助函数ODS(Output Debug String),它将调试信息输出到调试窗口。

protected void ButtonHelloWorldAsync_Click(object sender, EventArgs e) { ODS("Pre HelloWorldAsync..."); MyService.HelloWorldAsync(); ODS("Post HelloWorldAsync"); } public void EventHandler(object sender, localhost.HelloWorldCompletedEventArgs e) { ODS("EventHandler"); ODS("" + e.Result); } public static void ODS(string Msg) { string Out = String.Format("{0} {1}", DateTime.Now.ToString("hh:mm:ss.ff"), Msg); System.Diagnostics.Debug.WriteLine(Out); }

启动项目并点击按钮,可以看到调试输出窗口中的信息。异步调用立即返回,大约10秒后,事件处理程序触发并返回结果。这看起来是完美的,但事实并非如此。

在调用过程中,浏览器等待了10秒钟。尽管异步调用立即返回,但ASP.NET在事件触发之前等待页面渲染。这不是想要的结果。

为了解决这个问题,尝试了几种技术。如果检查自动生成的代理,会发现它包含了一个同步调用HelloWorld的方法,以及一组用于异步调用的方法。尝试了代理中提供的另一种异步方法。

protected void ButtonBeginHelloWorld_Click(object sender, EventArgs e) { ODS("Pre BeginHelloWorld..."); MyService.BeginHelloWorld(AsyncCallback, null); ODS("Post BeginHelloWorld"); } public void AsyncCallback(IAsyncResult ar) { string Result = MyService.EndHelloWorld(ar); ODS("AsyncCallback"); ODS("" + Result); }

代理中的BeginHelloWorld函数需要一个回调函数作为参数。测试后,调试输出窗口的信息如下:

04:40:58.57 Pre BeginHelloWorld...
04:40:58.57 Post BeginHelloWorld
04:41:08.58 AsyncCallback
04:41:08.58 Hello World

它与之前的方法工作方式相同,但有一个关键区别:页面在函数调用后立即渲染。担心页面对象在渲染页面后会被销毁,但系统足够智能,将页面对象保留在内存中以处理回调。

这两种技术都有各自的用途:

  • 延迟渲染:如果想验证信用卡、查找运费并确认商品是否库存,可以并行运行三个Web服务调用,而不渲染页面,直到所有服务都完成。这样很好。可以在所有服务完成后,将信息作为渲染页面的一部分发送回客户端。
  • 立即渲染:如果只想启动一个服务并返回给客户端,也可以做到。然而,页面在服务完成运行之前就被发送到客户端,所以将无法在服务完成运行时更新页面的部分。

总结:YourFunctionAsync()和EventHandler不会在处理程序触发之前渲染页面。BeginYourFunction()和CallBack函数将尽快渲染页面。

发现这一切都非常有趣,并且为了这个主题做了很多搜索和研究,但这方面的文档并不多。最大的线索是发送到WSDL.exe程序的参数:http://msdn.microsoft.com/en-us/library/7h3ystb6(VS.100).aspx。两个参数是oldAsync和newAsync。OldAsync将创建Begin/End函数;newAsync将创建Async/Event函数。

注意:没有尝试这个,但这篇文章中提到了。将确认这个作为学生的练习。

包含了创建的完整测试项目,以验证这些发现。该项目是用VS 2008 SP1创建的。有一个解决方案文件,包含3个项目,这3个项目是:

  • Web Service
  • ASP.NETApplication
  • Windows Forms Application

要决定运行哪个程序,只需右键单击一个项目并选择"Set as Startup Project"。

创建了Windows Forms应用程序,看看它是否会揭示任何秘密。发现在Windows Forms应用程序中,生成的代理不包括Begin/Callback函数。这些函数只为ASP.NET页面生成。可能是由于前面讨论的原因。也许那些微软的男孩和女孩知道他们在做什么。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485