We, as developers, must begin to use more asynchronous thinking when we develop applications. Processors don't get any faster, they just gets more cores that can do more work simultaneously, but that is no good for us if we don’t use it. Microsoft is for example working on ParallelFX, which probably will be included in .NET 4.0. There is really no reason to wait though and in this post I will cover one option on how you could do asynchronous calls to a web service in a windows forms application. Of course there are other ways to do this, for example by using BackgroundWorker.
The scenario is common for everyone, I guess, and the picture below shows the current synchronous implementation. The GetCompany function in the CompanyServiceAgent class returns a Company object that is then used to populate the controls in the Windows Form (View). The CompanyPresenter class is the Presenter in the MVP pattern, but it could just as well have been a form depending on how you prefer to structure your program.
With this structure the client application is locked for as long as it takes to complete the call to the service, and the cursor is not changed to a hourglass (without a call to DoEvents), an animated progress bar wouldn't update and the user can't do anything meaningful (just wait). So let's dig in how we could change this code to become asynchronous instead and solve all of the problems listed above and give the user a much more responsive and pleasant experience with the application.
The first step is to make sure that the asynchronous versions of the service methods is created, which they by default is not (strange default setting in my opinion). I have blogged about the Advanced settings in available in the "Add Service Reference" dialog here, and in that dialog there is an option called "Generate asynchronous operations" that must be checked to get the async methods. After doing that the GetCompany method in the service proxy will be accompanied with a BeginGetCompany and EndGetCompany methods. These are the async methods that we want to use from now on. But before moving on, let’s begin with the core of the synchronous version of GetCompany.
CompanyService.GetCompanyResponse response = service.GetCompany(request);
return response.Company;
The asynchronous BeginGetCompany method signature is the same as the synchronous, but also adds two more parameters to the end; a callback which is a delegate of type AsyncCallback, and an asyncState of type object. The callback is where we should provide the method that should be called when the asynchronous call is finished. This is all handled by the framework. The last argument is a way for you to send some extra information to the callback method, but that won't be necessary in this case because I will implement the callback as an anonymous method, which means that all local variables will be available anyway. Let's start with the basic structure for making the call asynchronous.
service.BeginGetCompany(request,
delegate(IAsyncResult result)
{
CompanyService.GetCompanyResponse response = service.EndGetCompany(result);
//TODO: Return result
}, null);So far there really aren't that much of a difference in how this is implemented compared to the synchronous version, but how should we return the result to caller (CompanyPresenter)? We can't use a return statement, becuase as soon as the BeginGetCompany call is called the execution will continue and return to the ComapnyPresenter (and this is what we wanted). We could use a callback parameter also for the CompanyServiceAgent.GetCompany, but I choose to instead add an event that the CompanyPresenter could listen for, and I have found that this structure that most programmers are comfortable with. Then the code would look something like this.
service.BeginGetCompany(request,
delegate(IAsyncResult result)
{
CompanyService.GetCompanyResponse response = service.EndGetCompany(result);
if (CompanyAvailable != null)
CompanyAvailable(this, new DataEventArgs<Company>(response.Company));
}, null);The DataEventArgs is a simple generic class that I have created to be able to easily return one typed result as shown below.
public class DataEventArgs<T> : EventArgs
{
public DataEventArgs(T data)
{
this.Data = data;
}
public T Data { get; private set; }
}The event is declared like this in the CompanyServiceAgent class.
public event EventHandler<DataEventArgs<Company>> CompanyAvailable;
The problem with this naive implementation though is that the callback from asynchronous operations is always on another thread and since none of the windows forms controls are thread safe we will have problem as soon as we access them from the method that catches the event. This means that we need to somehow switch to the UI thread and in .NET Framework 2.0 Microsoft added a very useful SynchronizationContext class that makes it easy to do this thread switching without requiring you to use the Control.Invoke method.
To help you with this I created a AsyncServiceAgent base class that wraps all the work and the new overall structure is shown in the picture below. DisplayCompany is the listener method called by the CompanyAvailable event. I have moved all code from LoadCompany that was executed after the call to GetCompany to DisplayCompany instead.
For those of you who have managed to read this far, let's wrap it all up by also showing the implementation of the AsyncServiceAgent class and the final implementation of the GetCompany method.
public abstract class AsyncServiceAgent
{
System.Threading.SynchronizationContext syncContext = System.Threading.SynchronizationContext.Current;
protected AsyncServiceAgent()
{
}
protected void RaiseDataEvent<T>(EventHandler<DataEventArgs<T>> theEvent, T data)
{
if (theEvent != null)
{
RaiseEvent(delegate(object nothing)
{
theEvent(this, new DataEventArgs<T>(data));
});
}
}
protected void RaiseEvent(System.Threading.SendOrPostCallback operation)
{
if (syncContext != null)
{
syncContext.Send(operation, null);
}
else
operation.Invoke(null);
}
}Notice that I store the current synchronization context when the class is instantiated so that I can be sure that it is the windows forms synchorinization context I will be using when switching thread in the RaiseEvent method. RaiseDataEvent is the method that will be used by the GetCompany method as shown below.
service.BeginGetCompany(request,
delegate(IAsyncResult result)
{
CompanyService.GetCompanyResponse response = service.EndGetCompany(result);
RaiseDataEvent<Company>(CompanyAvailable, response.Company);
}, null);It has been a long post, but I hope that I managed to show that it is not so hard to solve the "infrastructure" part of making the calls asynchronous. Counting number of lines the asynchronous service agent is not that much more work than the synchronous and in my opinion it is easy to follow with the anonymous method. What is required except for this? Well, a mind shift and to be careful with which code is run, that expects the data to already be available, because when doing it asynchronous it is not certain that it is. Also worth noting is that there are many parts of .NET Framework that supports the same basic structure with Begin- and End-methods as the generated proxy classes, so this pattern could be used for other things as well.
If you managed to read all through I hope it was worth it :)