Working With Asynchronous .NET Web Service Clients

While many developers realize that consuming Web Services using the asynchronous call mechanisms built into .NET is useful, they also find it confusing. If you've had trouble trying to use the asynchronous call methods in your generated proxies—or if you're wondering what asynchronous means in the first place—this article helps clarify some things and makes your programming tasks (at least as related to asynchronous processing) a little easier.

The general discussion focuses on consuming .NET Web Services, but because asynchronous processing is built into .NET, the information is applicable to any asynchronous processing you might want to perform. That is, when you create a proxy for your Web Service, the asynchronous methods are baked in. But, you also have similar asynchronous methods built into your .NET delegate classes and therefore also can utilize delegates in an asynchronous fashion (hmm, perhaps fodder for another article: using .NET delegates as the poor man's multithreaded processing platform?).

Web Service Proxies

To be clear about Web Services and their consumption, Figure 1 presents the basic architecture.

Figure 1: Generalized Web Service Communication Architecture

The ultimate goal of a Web Service is to give the client the illusion that remote resources and processing capabilities are actually present on the client computer. For this to occur, some software on the client must generate and issue the request (as well as interpret the response), and some software on the server must accept requests, translate them into formats the server can work with, and activate the server-side processing itself. The first of these pieces of software is known as the proxy, because it simulates the server on the client. The proxy communicates with some form of stub, whose job is to take the SOAP-based XML and convert it into binary information resident within the server's memory. The stub also initiates the server-side processing.

When you use .NET to consume Web Services, it can create this proxy for you automatically, either with Visual Studio or the tool that ships with .NET itself, Wsdl.exe. Either tool queries the Web Service for its service description, as described by the Web Service Description Language (WSDL), and then generates the proxy source code for you by interpreting the WSDL it finds.

If you open the proxy source code you've been given, you should find all of the Web-based methods exposed by the Web Service. But, you'll also find some curiously named methods beginning with "Begin" and "End" and ending with the Web methods you'd expect. That is, if the Web Service exposed a single method named "CalcPayment," in your proxy you would find executable code for not only CalcPayment itself, but also for BeginCalcPayment and EndCalcPayment. You will use these "begin" and "end" methods for your asynchronous processing.

Asynchronous Processing

What exactly is asynchronous processing and why should you be concerned about using it? To answer that, let me first ask you a question: How do you feel when an application's user interface locks for some extended period of time while it processes your selected action? Most of us tend to dislike that kind of application behavior—and I'm being kind here.

Instead, we prefer our user interfaces responsive, even when working on actions we know to be lengthy. If that action involved a call to a Web Service, especially one over the Internet (versus simply a local one found on our intranet), the request might take quite a bit of time to process. Delays from 200 milliseconds up to full seconds are not uncommon. For example, say a given Web Service takes on average of 600 milliseconds to complete, but the user waits over half a second for each call. That might not seem like much, but to a user it could prove to be a major annoyance. Remember, the user interface locks entirely for this call duration.

The process thread used to make the call to the Web Service causes the locking phenomenon. The user interface usually locks up because the thread servicing the user interface (button clicks, painting behavior, and so forth) is also the very same thread making an extended call somewhere else, waiting for data. It might be a Web Service call, but it also might be some lengthy local financial calculation, database query, or whatever. Anything that takes a significant amount of time to process, if serviced by the same thread that manages the user interface, will cause the user interface to cease taking inputs from the user and appear to be locked. In general, try to avoid this. Such behavior has been known to cause users to throw keyboards, break monitors, and worst of all, no longer purchase your software.

Getting back to asynchronous processing, my dictionary defines asynchronous as "pertaining to a transmission technique that does not require a common clock between the communicating devices." Overlooking the clock reference, you could rewrite this definition in its loosest terms to read something like "asynchronous processing is a form of multithreaded programming where a primary thread activates a lengthy process to be managed by a secondary thread." The primary thread's task is complete when the process is activated, so it can (almost immediately) return to its main function, which in this case might be managing the user interface. The secondary thread then begins the lengthy process and sustains the wait, a procedure known as blocking. It locks and ceases processing, waiting for the lengthy process to terminate. It then (optionally) reports back to the primary thread the results of the process.

A side benefit of this is that you then can easily make multiple such processing calls in parallel. If the primary thread made multiple calls (synchronously, on the same thread), each synchronous call would need to be made sequentially, thus increasing the processing time greatly.

Asynchronous .NET Web Requests

In the case of a Web Service called using the proxy's asynchronous call methods, the secondary thread is actually a thread from the .NET thread pool. What's great about that is you don't need to create and manage your own thread (or pool of threads, which is even more complicated). Instead, when you call the "begin" method found in your proxy, you pass into that a callback function .NET will use to provide the Web Service response to your application. Always remember that when your callback function is executed, the .NET thread executes it, not the primary thread (which is most likely the thread that created your user interface). I'll revisit this a bit later in this discussion.

Although not the only way to process asynchronous Web Service calls, I finalize the call to the Web Service by extracting the resultant parameter values, as well as the method return value, in the callback function. This finalization is the call to the "end" method found in your Web Service proxy. The "end" method retrieves the resulting parameter objects (out and ref parameters), as well as the return value, and provides them to you for further processing. The beauty is that your primary thread doesn't have to block while waiting for the Web Service to complete. Instead, a .NET thread is used, and your user interface is free to continue to accept user inputs and be just as responsive and snappy as it was prior to the Web Service invocation. Later, when the call is complete, you can marshal the data back to your user interface thread and do whatever is necessary at that point.

You could, if you desire, perform some wait processing in your main thread (a technique for this is described in the MSDN article "Communicating with XML Web Services Asynchronously"). Personally, I find this to be of limited value because you're still employing the primary thread to perform the wait processing. But, I would be remiss if I didn't at least mention it.

Asynchronous Behavior Is a Client-Side Phenomenon

No matter how you call your Web Service, synchronously or asynchronously, each call is the same to the Web Service. You're still sending in one SOAP packet, and you'll still (hopefully) receive a single SOAP packet in response.

Marshaling—a Side Note for Windows User Interface Programmers

Marshaling—what an ugly sounding word. It harkens back to the old COM programming days when we had to deal with passing data between threads. Oh, but that's exactly what we're doing here! Uh-oh. Actually, it's not so bad. Marshaling basically is the process of converting information for communication between threads.

Because Windows is a virtual, memory-based operating system, memory addresses are meaningless between processes. If you pass information between processes based upon that information's memory address, it will have no meaning in the other process and this second process likely will crash. Old COM programmers (and I include myself here) know this all too well.

Fortunately, .NET handles marshaling for you without too much effort on your part. The age-old Windows rule that only—and I mean only—the thread that created a window can update that window's state also helps. Therefore, you can't just change window text, color, shape, size, or political affiliation willy-nilly without making sure you're doing so from the thread that created the window. Forget this fact and Windows becomes unpredictable. Sometimes things work, and sometimes they don't. If you follow the basic rule, though, and always let the window's creating thread update the window, your code (and application) won't crash and burn.

Delegates in .NET

If you look into your MSDN documentation under System.Windows.Forms.Control, you'll find a curious method called Invoke(). The first sentence of the associated documentation page reads: "Executes a delegate on the thread that owns the control's underlying window handle." Wow, that sounds a lot like "the thread that created the window." But what's this "executes a delegate" stuff?

We're back into the guts of .NET, but this time we're looking at the delegate and its relationship to events and event handling. If you create a delegate that has as its parameters the very same parameters that the Web Service returned, .NET will marshal those parameters for you and ship them to the particular window's creation thread. There you have it! The parameters are now available to the window's creating thread, so at that point you can update the window to your heart's content. Easy, right?

The truth is it isn't too bad, once you've seen it in action. In .NET terms, a delegate is essentially a type-safe callback function. So, what you need to do utilize it is:

  1. Design a method in your application that accepts the same parameters the Web Service returned;
  2. Designate it as a delegate data type; and
  3. Create one of these delegates for use with Invoke().

I'll show some code for this shortly. But first, back to asynchronous Web Service calls in general.

Working With Asynchronous .NET Web Service Clients

Show Me the Code

I created two .NET projects for this article. One is a basic Web Service; the other is a Windows Forms client that calls the Web Service. The Web Service is a simple service that maintains a list of people and their phone numbers. It reads the information from a file and caches the file contents in the ASP.NET application cache for efficiency. The Web Method I call is named FindPhoneNumberForName.

The client is a Windows Forms application in this demonstration, but this isn't universally the case. Quite often, the client could be another service or perhaps some business logic in a large enterprise application. Any .NET consumer of a Web Service can use the techniques I show here. Figure 2 shows the user interface I created.

Figure 2: Demonstration User Interface

The user provides a name for which he or she desires a phone number, clicks "Get", and then waits for the response.

The interesting code is in the button click handler for the "Get" button:

private void cmdGet_Click(object sender, System.EventArgs e)
{
   // Create the Web Service proxy...
   YPServiceClass ws = new YPServiceClass();

   if ( tbName.Text.Length > 0 )
   {
      this.Cursor = Cursors.AppStarting;
      try
      {
         // Make the asynchronous call
         ws.BeginFindPhoneNumberForName(tbName.Text,
            new AsyncCallback(lookupHandler),ws);
      } // try
      catch (Exception ex)
      {
         string strMsg =
            String.Format("Error initiating asynchronous " +
                          "call: '{0}'",ex.Message);
         MessageBox.Show(strMsg, "Web Error",
                         MessageBoxButtons.OK,
                         MessageBoxIcon.Error);
         this.Cursor = Cursors.Arrow;
      } // catch
       // if
   else
   {
      lblPhone.Text = "";
   } // else
}

I italicized the actual call to the Web Service. Note that unlike a synchronous call, which would look like this:

ws.FindPhoneNumberForName(tbName.Text);

it has a very different invocation that looks like this:

ws.BeginFindPhoneNumberForName(tbName.Text,
     new AsyncCallback(lookupHandler),ws);

You know it's an asynchronous call because I use the "begin" form of the method's signature, but other parameters go into the request also. The first is an instance of an AsyncCallback object, and the second is my instance of the Web Service proxy itself. The AsyncCallback object is actually a .NET delegate defined specifically for asynchronous callbacks. It defines the method signature for a method that you provide to handle the returned information from the Web Service. The delegate defines a method signature like the following:

delegate void AsyncCallback(IASyncResult iar);

If you generate a method with the preceding signature and provide that to the constructor of the AsyncCallback delegate (as I did in the Web Service invocation), when the Web Service response arrives from the remote server, .NET will invoke the method you provided to the delegate's constructor. In this case, .NET will invoke the method I named lookupHandler.

The lookupHandler method is then where I actually strip out the results of the Web Service invocation:

private void lookupHandler(IAsyncResult iar)
{
   try
   {
      // Finalize the asynchronous call
      string number = ((YPServiceClass)iar.AsyncState).
         EndFindPhoneNumberForName(iar);

      // Update the user interface
      object[] args = new object[1];
      args[0] = number;
      this.Invoke(new LookupUIUpdateHandler(lookupUIUpdateHandler),
         args);
   } // try
   catch (Exception ex)
   {
      string strMsg = 
         String.Format("Error terminating asynchronous " + 
         "call: '{0}'",ex.Message);
      MessageBox.Show(strMsg,"Web Error",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Error);
   } // catch
}

I italicized the magic line of code in lookupHandler for extracting the resulting Web Service return parameter information, but I'll repeat it here because it looks a little strange:

string number =
   ((YPServiceClass)iar.AsyncState).EndFindPhoneNumberForName(iar);

As you may recall, when we invoked the Web Service asynchronously, we passed our parameters, an AsyncCallback object, and a reference to the Web Service proxy itself into the "begin" method. We provided the proxy reference to a call implemented by the proxy itself because an object must be responsible for maintaining the state of the asynchronous call. In other words, some object .NET can control must contain the results of the call so that .NET can forward those results to us. This is the job of the proxy: the proxy implements the "end" (call finalization) method. If you write your own asynchronous calls, you may have to provide your own asynchronous state object. But in the case of .NET Web Services and its proxies, you use the proxy objects themselves.

So, the following piece of code does nothing more than return the original proxy object:

((YPServiceClass)iar.AsyncState

And becaise it's our Web Service's proxy, it implements the "end" method, EndFindPhoneNumberForName. So, all we really did was perform a simple cast to obtain our proxy and then finalize the call. The return value for EndFindPhoneNumberForName is simply the return value of the phone number request method, which is a string representing the phone number of the individual requested.

If I were calling this Web Service code and didn't need to display the string in a window, I'd be done. But in the lookupHandler method, some additional code is necessary because I am sending this string to a window for display. Remember, only the thread that created the window can change its state, and when .NET calls back with the result of an asynchronous Web Service call, the callback is executing on a .NET thread pool thread. So, I must marshal the string from the .NET thread pool thread to my application's user interface thread before I can display it. If I don't, the application might not work. The only correct thing to do is marshal the data, even if it is just a simple string.

As I mentioned previously, .NET accomplishes marshaling by its eventing environment and the Control.Invoke method. The following code sets up the call to Invoke:

object[] args = new object[1];
args[0] = number;
this.Invoke(new LookupUIUpdateHandler(lookupUIUpdateHandler), args);

The LookupUIUpdateHandler follows the .NET guidelines for triggering Invoke events. It defines a method that accepts the type and number of parameters contained within args. For this example, that's merely a single string input:

delegate void LookupUIUpdateHandler(string number);

To the delegate's constructor, I passed a reference to the method that implements the delegate, lookupUIUpdateHandler:

private void lookupUIUpdateHandler(string number)
{
   // Update the window
   lblPhone.Text = number != null ? number : "(no number listed)";

   // Reset the cursor (previously set when async call
   // was made).
   this.Cursor = Cursors.Arrow;
}

And, it is here I finally update the user interface because .NET guarantees me that at this point I'll be on the proper thread for managing windows in my application.

Wrap Up

And there you have it: .NET provides a pool of threads that you can use for asynchronous client-side Web Service processing. The way you get to these threads is to use the special "begin" and "end" methods .NET generates when it reads the service description language. You'll find these methods in the resulting proxy file created when adding a Web reference to your project.

By using one of these internal .NET threads, you can free your primary process thread (the one that most likely created your user interface if you're calling the Web Service from a Windows Forms application). The secondary thread blocks while waiting for the Web Request to return, allowing the primary thread to continue performing its main processing tasks (such things as painting, animation, and button/text inputs). This is the essence of asynchronous processing, and it's quite effective. Give it a try!



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds