Have a web service or page that calls another web service? Make it async

When Jeff Richter came to the Atlanta office, we were fortunate enough to hold him hostage in the Atlanta office for a small get-together of folks from the field.  One of the questions that came up is how web pages calling other web pages affects perf.  The bottom line that came out of the discussion is that ASP.NET pages or ASP.NET web services that call out to other web services should absolutely, positively do this asynchronously.  Of course, we were privileged to see an in-depth discussion of how I/O completion ports work and where threads are actually (and not) being used.  I asked Jeff to *please* write an article on it for MSDN as that would be a great point to show how things really work and why.

OK, so you need to make asynch calls from your ASP.NET page.  You could do this in ASP.NET 1.1, but with 2.0 it becomes SO much simpler due to the addition of the xxxAsync and xxxCompleted methods in the SoapHttpClientProtocol generated proxy.  We need to mark the ASP.NET page as Async by adding this attribute to the Page directive in the .aspx page:

<%@ Page Language="C#" Async="true" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %>

The next thing we do is to use the xxxAsync and xxxCompleted methods to call our service.  While doing this, make sure to check the error condition to avoid the Yellow Screen of Death in your ASP.NET app.

     protected void Page_Load(object sender, EventArgs e)
    {
        localhost.Service s = new localhost.Service();
        s.HelloWorldCompleted += new localhost.HelloWorldCompletedEventHandler(s_HelloWorldCompleted);
        s.HelloWorldAsync();        
    }

    void s_HelloWorldCompleted(object sender, localhost.HelloWorldCompletedEventArgs e)
    {
        if (null != e.Error)
        {
            this.Title = e.Error.Message;
        }
        else
        {
            this.Title = e.Result;
        }        
    }

In this example, you can see that we check for the error condition prior to obtaining the result. If you just obtained the result, you would get the SOAP exception back immediately.  The natural first inclination is to then wrap the result accessor with a try/catch block, which means the exception would need to be re-thrown from the client and subsequently caught. And we all know we should avoid exceptions as much as possible due to the hit on perf.  In this case, you can avoid the hit on perf just using this minor code change.