在现代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
它与之前的方法工作方式相同,但有一个关键区别:页面在函数调用后立即渲染。担心页面对象在渲染页面后会被销毁,但系统足够智能,将页面对象保留在内存中以处理回调。
这两种技术都有各自的用途:
总结: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个项目是:
要决定运行哪个程序,只需右键单击一个项目并选择"Set as Startup Project"。
创建了Windows Forms应用程序,看看它是否会揭示任何秘密。发现在Windows Forms应用程序中,生成的代理不包括Begin/Callback函数。这些函数只为ASP.NET页面生成。可能是由于前面讨论的原因。也许那些微软的男孩和女孩知道他们在做什么。