Calling Web Services Asynchronously
Enhance Performance and Increase Efficiency
October 30, 2009
RELATED: "Practice Safe Web Services" and "Asynchronous Calls to Web Services."
"When first introduced, Web services presented a novel way to exchange data between distributed systems. Over the years, Web services have matured tremendously and have moved out of the realm of simply being novel and into the realm of being enterprise-capable. As a result, more and more companies around the world are using Web services to integrate their systems.
As the use of Web services increases, the importance of knowing how to consume services in a scalable and efficient manner becomes paramount, especially given that services can be called synchronously and asynchronously. Applications making synchronous Web service calls send data to a service and wait for the results to return before continuing (see Figure 1). Asynchronous calls, on the other hand, allow multiple Web service calls to be made while the application continues to execute other operations, such as querying a database or reading a file (see Figure 2). As a result, asynchronous calls can increase an application s overall performance in some cases. This increase is typically seen in applications running on multi-CPU servers. A perceived increase in performance by end users may also be seen on single-CPU client systems (those running Windows Forms applications, for instance) when asynchronous techniques are used.
Figure 1: Synchronous Web service calls result in an application waiting until a Web service call returns. While these types of calls work well in many situations, they can be inappropriate when a Web service takes a variable amount of time to return data or when multiple Web services must be called in parallel.
Figure 2: Asynchronous calls allow a Web service to be called while the application continues executing other tasks. It can be useful in cases where multiple Web services need to be called simultaneously or when a Web service call may take a variable amount of time to return.
Synchronous Web Service Calls
Before analyzing the different ways .NET applications can make asynchronous Web service calls, let's take a quick look at how to call a service synchronously. If you've generated a Web service proxy class using Visual Studio .NET or the wsdl.exe command-line utility and used it to call a Web service, then you've more than likely made a synchronous call before. Synchronous calls require little coding and offer the simplest way to consume a Web service. For example, the code that follows calls a Web service that returns customer information through a proxy class named CustomersOrders:
//Create Web Service proxy objectCustomersOrders proxy = new CustomersOrders();//Call Web ServiceCustomerDetails details = proxy.GetCustomerData("ALFKI");//Use data returned from Web Servicethis.txtOutput.Text = details.ContactName + " " + details.Address;
When the synchronous call is made to GetCustomerData, the application pauses (this pause is more officially known as blocking ) until the Web service returns data. If the Web service takes 30 seconds to respond, then the application must wait 30 seconds before continuing on to the next line of code (again, refer to Figure 1). While this may not present a problem for back-end applications (ones with no user interface), it can present a significant problem if a user is involved because the user may think that the application has locked-up, when, in reality, it is simply busy waiting to hear back from the Web service.
Fortunately, asynchronous Web service calls do not block the application from performing additional operations. Let s take a look at a few of the different options available for making asynchronous calls in .NET version 2.
Asynchronous Web Service Calls
Although not as simple as synchronous Web service calls, asynchronous calls (also known as async calls) made from Windows Forms and ASP.NET Web Forms can result in more performant and efficient applications. Referring back to Figure 2, an asynchronous Web service call is made on a separate thread from the one on which the application is running. This allows the application to make the call and continue with other operations, such as consuming additional Web services or performing database queries. Once the Web service call returns, the application is notified and can act upon the data.
You may wonder how asynchronous calls can benefit ASP.NET applications since data returned from a Web service must be accessible before sending a completed page back to the browser. After all, it wouldn't do much good to make an asynchronous call to a Web service, continue sending the page to the end user, then receive the data back from the Web service after the page has been sent to the browser. Asynchronous calls made within ASP.NET applications can allow multi-tasking to occur, which can enhance an application's performance in many situations. This is a result of being able to make multiple calls to services in a parallel manner, rather than serially.
There are several different ways to make asynchronous calls to Web services in .NET version 2. In general, there are five ways to make an asynchronous Web service call using the .NET Framework:
Async Callbacks
WaitHandles
Polling
Events
Async Pages
In this article I'll introduce you to async callbacks, WaitHandles, and events as well as explain how they can be used in Windows Forms and ASP.NET applications.
Async Callbacks and WaitHandles
Async callbacks have been around since .NET version 1 and are a common way to call a Web service asynchronously. The process of making an async callback starts with calling a Web service proxy object s BeginXXX method, where XXX represents the actual method being called (BeginGetCustomerData, for instance). When calling BeginXXX, you supply the parameter data needed by the Web service, an AsyncCallBack instance that points to a callback method, and any state information that the callback method may need to use.
When a Web service returns data, the method identified using the AsyncCallBack object mentioned earlier is called. Within this method the proxy object's EndXXX method (EndGetCustomerData, for example) is called to access the data returned from the Web service.
An example of using the async callback technique in an ASP.NET code-behind class is shown in Figure 3. Looking through the code you ll notice that the callback method (named CallBack in this example) accepts an IAsyncResult object as a parameter. The IAsyncResult object encapsulates any data returned from the Web service, as well as a reference to the proxy object that initiated the call.
protected void btnSubmit_Click(object sender, EventArgs e) { CustomersOrders proxy = new CustomersOrders(); //Create AsyncCallback object and specify method to call AsyncCallback callback = new AsyncCallback(this.CallBack); //Call Web Service asynchronously proxy.BeginGetCustomerData("ALFKI", callback, proxy); this.txtOutput.Text += "Web Service called asynchronously.";}private void CallBack(IAsyncResult ar) { //Access proxy object that made call through AsyncState CustomersOrders proxy = ar.AsyncState as CustomersOrders; if (proxy != null) { //Access data returned from Web Service CustomerDetails details = proxy.EndGetCustomerData(ar); this.txtOutput.Text += "Async callback reached."; this.txtOutput.Text += "Data returned from Web Service: " + details.ContactName + Environment.NewLine; }}
Figure 3: This code example shows how to make asynchronous Web service calls using the async callback technique.
If you run this code in an ASP.NET Web Form, you may notice that the data returned from the Web service isn t always displayed in the TextBox server control when the page renders. Why is this? The asynchronous callback is returning data properly; however, you never know if the CallBack method will be reached before the page completes executing, since the call is made on a separate thread. Sometimes the data may be returned before the Page object finishes its lifecycle; other times the data may be returned after the page has already been sent to the browser.
To remedy this problem in ASP.NET applications, you can force the Page object to wait until the Web service returns data by using a WaitHandle. When a call is made to the proxy object's BeginGetCustomerData method, an IAsyncResult object is returned. This object exposes several properties, including AsyncWaitHandle, that can be used to block the page from sending content back to the browser until the Web service returns data. AsyncWaitHandle exposes a WaitOne method that causes the blocking operation to occur. An example of using IAsyncResult and the WaitOne method is shown in Figure 4.
protected void btnSubmit_Click(object sender, EventArgs e) { CustomersOrders proxy = new CustomersOrders(); AsyncCallback callback = new AsyncCallback(this.CallBack); //BeginGetCustomerData() returns an IAsyncResult object IAsyncResult ar = proxy.BeginGetCustomerData("ALFKI", callback, proxy); this.txtOutput.Text = "Web Service called asynchronously."; //Block page from completing until Web Service returns data ar.AsyncWaitHandle.WaitOne();}private void CallBack(IAsyncResult ar) { //Access proxy object that made call through AsyncState CustomersOrders proxy = ar.AsyncState as CustomersOrders; if (proxy != null) { //Access data returned from Web Service CustomerDetails details = proxy.EndGetCustomerData(ar); this.txtOutput.Text += "Async callback reached."; this.txtOutput.Text += "Data returned from Web Service: " + details.ContactName + Environment.NewLine; }}
Figure 4: Using the IAsyncResult object s AsyncWaitHandle to force the page to wait for a Web service call to return before sending the page output to the browser.
More than one Web service can be called asynchronously from an ASP.NET Web Form by using multiple AsyncCallback objects. They can also be called by creating an array of WaitHandle objects. This can be done by adding each service's IAsyncResult object into a WaitHandle array. Once the array is created, the WaitHandle class static WaitAll method can be called to block the application from completing until all Web service calls have returned. After all the services return, their respective EndXXX methods can be called to access the data. Figure 5 shows an example of creating a WaitHandle array and using the WaitAll method.
//Call 2 Web Services asynchronouslyCustomersOrders proxy = new CustomersOrders();CustomersOrders proxy2 = new CustomersOrders();IAsyncResult ar = proxy.BeginGetCustomerData("ALFKI", null, null);IAsyncResult ar2 = proxy2.BeginGetCustomerData("DUMON", null, null);this.txtOutput.Text += "Async services called. ";//Create WaitHandle arrayWaitHandle[] w = new WaitHandle[]{ ar.AsyncWaitHandle, ar2.AsyncWaitHandle };//Wait on blocked Web Service threadsWaitHandle.WaitAll(w);//Access data returned from Web ServicesCustomerDetails details = proxy.EndGetCustomerData(ar);CustomerDetails details2 = proxy2.EndGetCustomerData(ar2);this.txtOutput.Text += "Async waithandle (item 0 in array) " + "finished. " + It returned: " + details.ContactName + Environment.NewLine;this.txtOutput.Text += "Async waithandle (item 1 in array) " + "finished. It returned: " + details2.ContactName + Environment.NewLine;
Figure 5: Calling multiple Web services asynchronously can make a page render its content faster because each call is made in a parallel rather than in a serial manner. This example shows how to use the static WaitAll method to block the Page object from finishing until all Web service calls return.
Asynchronous Events
.NET version 2 provides a new way to call Web services in an asynchronous manner that leverages delegates and events in a way in which many .NET 1.1 developers are comfortable. Rather than relying on the AsyncCallback object, this new event-driven technique allows you to hook up a delegate to a Web service proxy object event. When the Web service call returns, the delegate points the data to the appropriate event handler to process the data.
Using the new event-driven asynchronous model is quite simple, given that the bulk of the work is done for you by the proxy object. Web service proxy code generated using Visual Studio .NET 2005 or the wsdl.exe tool now adds an XXXCompleted event within the class (GetCustomerDataCompleted, for example), as well as a method named XXXAsync (GetCustomerDataAsync, for example). The proxy code generation process also creates a delegate named XXXCompletedEventHandler, as well as an event arguments class named XXXCompletedEventArgs.
Follow the steps outlined below to use these new features:
1) Write an event handler that accepts Object and XXXCompletedEventArgs types as parameters.
2) Create an instance of the Web service proxy object.
3) Hook the proxy object s XXXCompleted event to the event handler (created in Step 1) using the XXXCompletedEventHandler delegate.
4) Call the XXXAsync method to start the call.
5) Handle data returned from the Web service within the event handler by accessing the XXXCompletedEventArgs object s Result property.
Figure 6 shows an example of using this new event-driven technique to call two Web services asynchronously. The output of the call is shown in Figure 7. Notice that each service is called on a separate thread.
protected void btnSubmit_Click(object sender, EventArgs e) { //Create first Web Service proxy object CustomersOrders proxy = new CustomersOrders(); //Hook the proxy event to an event handler proxy.GetCustomerDataCompleted += new GetCustomerDataCompletedEventHandler(proxy_GetCustomerDataCompleted); //Call the Web Service asynchronously proxy.GetCustomerDataAsync("ALFKI"); //Create second Web Service proxy object CustomersOrders proxy2 = new CustomersOrders(); proxy2.GetCustomerDataCompleted += new GetCustomerDataCompletedEventHandler(proxy2_GetCustomerDataCompleted); proxy2.GetCustomerDataAsync("DUMON"); this.txtOutput.Text = "Web Service called asynchronously." + Environment.NewLine;}void proxy_GetCustomerDataCompleted(object sender, GetCustomerDataCompletedEventArgs e) { CustomerDetails details = e.Result; this.txtOutput.Text += "Async event handler reached. Thread ID " + System.Threading.Thread.CurrentThread.ManagedThreadId + Environment.NewLine; this.txtOutput.Text += "Data returned from Web Service: " + details.ContactName + Environment.NewLine;}void proxy2_GetCustomerDataCompleted(object sender, GetCustomerDataCompletedEventArgs e) { CustomerDetails details = e.Result; this.txtOutput.Text += "Async event handler 2 reached. Thread ID " + System.Threading.Thread.CurrentThread.ManagedThreadId + Environment.NewLine; this.txtOutput.Text += "Data returned from Web Service: " + details.ContactName + Environment.NewLine;}
Figure 6: Asynchronous Web service calls can be handled using delegates and events in .NET version 2.
Figure 7: The output generated in this screenshot shows how asynchronous Web services are called on different threads. This allows services to be executed in parallel rather than waiting for one service to return before another service can be called.
Conclusion
Web services provide a loosely-coupled framework for exchanging messages between distributed systems. Knowing how to call one or more services can be important when architecting Windows Forms or ASP.NET applications. In this article, you ve seen several different ways to call Web services using asynchronous techniques, including async callbacks, WaitHandles, and events.
In addition to the techniques discussed, there are several other ways to call Web services asynchronously, including polling and asynchronous ASP.NET pages. The downloadable sample code for this article demonstrates these techniques.
The sample code accompanying this article is available for download.
About the Author
You May Also Like