Creating a Simplified Asynchronous Call Pattern for Windows Forms Applications

 

David Hill
Microsoft Corporation

March 2004

Summary: By way of a blog entry, David Hill explains how you can implement an asynchronous call pattern that allows you to consume Web services from a Windows Forms application without having to worry about threads. (8 printed pages)

Download the AsyncServiceAgent.msi sample file.

Note This article is derived from a blog entry at https://Weblogs.asp.net/dphill/. As such, the information in this article is provided "AS IS" with no warranties, and confers no rights. This article does not represent the thoughts, intentions, plans or strategies of Microsoft. It is solely the opinion of the author. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose. The original blog entry was edited for readability and Microsoft style guidelines.

Contents

Introduction Service Agents The UI Thread The .NET Asynchronous Call Pattern A Simplified Asynchronous Call Pattern Is It Worth It?

Introduction

I have written a number of smart client applications recently that employ some form of asynchronous call behavior to prevent the UI freezing while the application makes a Web service call in the background. Now, it's true that the .NET Framework provides a generic pattern for making asynchronous calls, but I find that this is sometimes a little unwieldy to use from within a Windows Forms application because of the need to ensure that the UI thread is used correctly.

In this article, I'll describe how you can implement a simpler asynchronous call pattern that allows you to consume Web services from a Windows Forms application without having to worry about how background threads interact with the UI thread ever again.

Service Agents

Visual Studio® .NET generates a nice Web service proxy class that allows the Web service to be called asynchronously, but this proxy class implements the generic .NET Framework asynchronous call pattern which, as I illustrate below, is rather inconvenient to use from a Windows Forms application. For this reason, I generally don't use the generated proxy classes directly, but instead employ an intermediate Service Agent class.

Service agents are classes that provide additional functionality that help a client interact with a Web service. Service agents can implement many useful features, such as data caching, security credential management, offline operation support, and so on. The Service Agent class created in this article provides a much simpler asynchronous call pattern than what is provided by the proxy class.

I could have built the additional functionality into the generated proxy class directly, but I like to leave the proxy class exactly as generated by Visual Studio and only change it by hand when absolutely necessary. Apart from anything else, this prevents me from losing code when I refresh the Web reference. The Service Agent class uses the generated Web service proxy class to make the actual Web service calls.

The UI Thread

An application starts off with one thread that is used to create and manage the user interface. This thread is called the UI thread. A developer's natural instinct is to use the UI thread for everything, including making Web service calls, remote object calls, calls into the database, and so on. This can lead to major usability and performance issues.

The problem is that you can never reliably predict how long a Web service, remote object, or database call takes. And if you make such a call on the UI thread, there will come a time when the UI locks up and you have irritated the user to no end.

Naturally, you would want to do these kinds of calls on a separate thread, but I would go a step further and say that you should do all non-UI related tasks on a separate thread. I am firmly of the opinion that the UI thread should be used solely for managing the UI and all calls to objects where you can't absolutely guarantee sub-second (or better) response times should be asynchronous, be they in-process, cross-process, or cross-machine.

In any case, to help make asynchronous calls easier to handle from the UI thread, I have been playing with a simplified asynchronous call pattern that looks something like the one that will be available in the Visual Studio 2005. To start with, let's examine how the normal .NET Framework asynchronous call pattern works.

The .NET Asynchronous Call Pattern

An object that supports the.NET Framework asynchronous call pattern, such as a generated Web service proxy class, has a Begin and an End method for each exposed Web method. To make an asynchronous call, a client calls the Begin method, which returns immediately, or at least once it has setup a separate thread to make the actual Web service call. At some later point in time, the client calls the End method when the Web service call has been completed.

When does the client know when to call the End method? The Begin method returns an IAsyncResult object that you can use to track the progress of the asynchronous call, and you could use this object to explicitly wait for the background thread to finish, but doing this from the UI thread defeats the whole point of doing the work synchronously. A better approach, for a process with a user interface, is to register a callback so that you are notified automatically when the work is completed.

Let's look at some sample code. In this example, say we want to retrieve some customer data from a Web service asynchronously. We have a Web service proxy object and it supports a Web method named GetCustomerData. We can start the Web service call and register for a callback using the following code, which we assume is invoked on the UI thread in response to some user interaction with the application's user interface.

private void SomeUIEvent( object sender, EventArgs e )
{
   // Create a callback delegate so we will
   // be notified when the call has completed.
   AsyncCallback callBack = new
      AsyncCallback( CustomerDataCallback );

   // Start retrieving the customer data.
   _proxy.BeginGetCustomerData( "Joe Bloggs", callBack, null );
}

Where CustomerDataCallback is the method that gets called when the Web service call finally returns. In this method, we need to call the End method on the Web service proxy object to actually retrieve the customer data. We might implement this method like so:

public void CustomerDataCallback( IAsyncResult ar )
{
      // Retrieve the customer data.
      _customerData = _proxy.EndGetCustomerData( ar );
}

Now, it is important to note that this method is called on the background worker thread. If we want to update the UI with the newly obtained data (say we want to update a data grid control to display the customer data), we have to be careful to do this on the UI thread. If we don't, then all manner of strange things may happen and we will have a difficult time diagnosing which bug to fix.

So how do we switch threads? Well, we can use the Service Agent method, which all Control derived objects implement. This allows us to specify a method that is called on the UI thread, and that method is where we can safely update our user interface. To use the Control.Invoke method, we have to pass a delegate to our UI update method. Our CustomerDataCallback method would then look something like this:

public void CustomerDataCallback( IAsyncResult ar )
{
      // Retrieve the customer data.
      _customerData = _proxy.EndGetCustomerData( ar );

      // Create an EventHandler delegate.
      EventHandler updateUI = new EventHandler( UpdateUI );

      // Invoke the delegate on the UI thread.
      this.Invoke( updateUI, new object[] { null, null } );
}

The UpdateUI method might be implemented like this:

private void UpdateUI( object sender, EventArgs e )
{
      // Update the user interface.
      _dataGrid.DataSource = _customerData;
}

While this is not exactly rocket science, I find the need to make such a 'double hop' needlessly complicated. The problem is that the original caller of the asynchronous method (that is, the WinForm class in this example) is made to be responsible for switching threads, and this requires using another delegate and the Control.Invoke method.

A Simplified Asynchronous Call Pattern

One technique that I often employ to lessen the complexity, and the amount of code required, of making asynchronous calls is to factor the thread switching and delegate stuff into an intermediate class. This makes it easy to make asynchronous calls from the UI class without having to worry about such things as threads and delegates. I call this technique Auto Callback. Using this technique, the example above would look like so:

private void SomeUIEvent( object sender, EventArgs e )
{
      // Start retrieving the customer data.
      _serviceAgent.BeginGetCustomerData( "Joe Bloggs" );
}

Once the Web service call completes, the following method is invoked automatically.

private void GetCustomerDataCompleted( DataSet customerData )
{
      // This method will be called on the UI thread.
      // Update the user interface.
      _dataGrid.DataSource = customerData;
}

The name of the callback method is inferred from the name of the original asynchronous call (so there is no need for constructing and passing a delegate), and it is guaranteed to call on the correct thread (so there is no need to use Control.Invoke). This approach is simpler and less error-prone.

Now, there's no such thing as a free lunch, so where is all the magic code that enables this much simpler model? Well, it's built into the ServiceAgent class, which looks like this:

public class ServiceAgent : AutoCallbackServiceAgent
{
      private CustomerWebService _proxy;

      // Declare a delegate to describe the autocallback
      // method signature.
      private delegate void
          GetCustomerDataCompletedCallback( DataSet customerData );

      public ServiceAgent( object callbackTarget )
            : base( callbackTarget )
      {
            // Create the Web service proxy object.
            _proxy = new CustomerWebService();
      }

      public void BeginGetCustomerData( string customerId )
      {
      _proxy.BeginGetCustomerData( customerId,
            new AsyncCallback( GetCustomersCallback ), null );
      }

      private void GetCustomerDataCallback( IAsyncResult ar )
      {
            DataSet customerData = _proxy.EndGetCustomerData( ar );

      InvokeAutoCallback(   "GetCustomerDataCompleted",
      new object[] { customerData },
      typeof( GetCustomersCompletedCallback ) );
      }
}

The service agent is easy to write in this case and does not require much code. It is fully reusable, so we only need to write it once and we can use it from any WinForm class. We are effectively shifting the responsibility for managing the threading issues away from the developer of the client code and on to the developer of the object that provides the asynchronous call API. Which in this case is the service agent, which is where it belongs. We can of course use this technique on any object that provides an asynchronous API.

The AutoCallbackServiceAgent base class is a simple generic class that implements the InvokeAutoCallback method. It looks like this:

public class AutoCallbackServiceAgent
{
      private object _callbackTarget;

      public AutoCallbackServiceAgent( object callbackTarget )
{
            // Store reference to the callback target object.
      _   callbackTarget = callbackTarget;
      }

      protected void InvokeAutoCallback(   string methodName,
         object[] parameters,
         Type delegateType )
      {
            // Create a delegate of the correct type.
            Delegate autoCallback =
                  Delegate.CreateDelegate( delegateType,
                         _callbackTarget,
                         methodName );

      // If the target is a control, make sure we
      // invoke it on the correct thread.
      Control targetCtrl = _callbackTarget as
            System.Windows.Forms.Control;
      if ( targetCtrl != null && targetCtrl.InvokeRequired )
      {
            // Invoke the method from the UI thread.
            targetCtrl.Invoke( autoCallback, parameters );
      }
      else
      {
            // Invoke the method from this thread.
            autoCallback.DynamicInvoke( parameters );
      }
      }
}

This code creates a delegate to the callback method and then decides whether to invoke it on the calling thread or the UI thread. If the invocation target is a control-derived object, then it invokes the callback method on the UI thread if required.

For those interested in such details, if you look at the code closely, you might see that it can be simplified if we didn't have to specify the auto callback delegate in the derived class. If we didn't need to specify the signature of the callback delegate, we could handle pretty much everything automatically and the derived service agent class would only need to implement the single line of code in the BeginGetCustomerData method.

Why do we need to specify this delegate? Well, it turns out that if we want to use the Control.Invoke method, we need a delegate. It is unfortunate that the .NET Framework developers didn't provide a version of this method that took a MethodInfo object because that would have made life a lot easier when writing generic code.

An alternative approach is to specify a well-known delegate type and use it for all callback method signatures. For instance, we could mandate that all auto callback methods have a method signature that takes a generic array of objects and passes the Web service parameters back to the client that way. The delegate declaration might look like the following:

public delegate void AutoCallback( object[] parameters );

Using this delegate would mean that we can dramatically simplify the service agent code, but would have to cast the returned data to the expected type in the client code.

Is It Worth It?

Is it worth all the hassle to implement a service agent class like this one? Well, it depends on how much you want to simplify the lives of the UI developers. Writing a service agent class like this one doesn't necessarily mean less code, but it does represent a better distribution of responsibilities between the UI and service agent developers.

As well as providing a simpler asynchronous call pattern, we can add other interesting functionality to the service agent class. I'll be building on these basic service agent ideas in the future to show you how to implement some interesting functionality into the service agent, such as automatic local data caching. Building such functionality into the service agent class means that the UI developer has a lot less to worry about moving forward.