ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Calling Web Services Asynchronously

by Raj Makkapati
08/01/2005

Making synchronous calls to web services can be problematic on occasion, because they have the potential to cause considerable delay. The reason for this is the manner in which synchronous calls work. The application blocks the client until the web service call returns. To overcome the necessity of having to wait for the web service response, we can call web services asynchronously. The asynchronous call mechanism does not need any additional functionality for the web service to handle it competently. The decision on whether to call synchronously or asynchronously belongs to the client.

Currently there are two methods of performing asynchronous calls:

  1. CallBacks
  2. WaitHandles

Here are a couple of web service web methods that we will be using in the demo. Two web methods, MyWebMethod and CreateXMLFile, are declared in the web service. MyWebMethod method takes a string parameter and concatenates "You speak " to it and returns the result. The CreateXMLFile method takes a string parameter and creates an XML file in the C://temp directory:

// [C# Code]
[WebMethod()]
public string MyWebMethod(string lang)
{
  System.Threading.Thread.Sleep(3000);
  return "You speak " + lang;
}

[SoapDocumentMethod(OneWay=true)]
[WebMethod()]
public void CreateXMLFile(string lang)
{
   // To set a 3 second thread sleep
   System.Threading.Thread.Sleep(3000);
         
   // Create an XMLDocument instance
   XmlDocument doc = new System.Xml.XmlDocument();

   // Load the Xml String into XmlDocument
   doc.LoadXml("<Root xmlns=\"http://CreateXMLFile\">
		<Resultxmlns=\"\">"+lang+"</Result></Root>");
         
   // Save the document
   doc.Save("C:\\temp\\MyFile1.xml");
}

Before we jump off to writing asynchronous calls, let us take a look at how the synchronous call is made so that we can better understand the asynchronous call mechanism.

Real World Web Services

Related Reading

Real World Web Services
Integrating EBay, Google, Amazon, FedEx and more
By Will Iverson

Synchronous Call

// Create an instance of the WebService
localhost.MyAsyncWebService webServ = 
   		new localhost.MyAsyncWebService();

// Return the result string
strResult = webServ.MyWebMethod("English");

The synchronous call mechanism is pretty straightforward; all we would need to do would be to declare an instance of the web service. As the web service is running locally, we would need to append the string "localhost" to the web service name. Once the web service is declared, web methods can be called as demonstrated above. Now let us start writing asynchronous calls.

Asynchronous Call using an AsyncCallback

In this approach, we create a delegate which can be invoked during runtime when the results from the web service are returned. Here's how it works.

// Button Click event
private void AsyncCallUsingCallBackBtn_Click
	(object sender, System.EventArgs e)
{
   string mLangInput = "English";

   // Create an instance of the WebService
   localhost.MyAsyncWebService webServ = 
		new localhost.MyAsyncWebService();

   // Create a delegate to handle the callback
   AsyncCallback asyncCall = 
		new AsyncCallback(CallbackSampleMethod);

   // Make an Asynchronous Call by calling 
   // the Begin method of the proxy class
   webServ.BeginMyWebMethod(this.mLangInput, asyncCall, webServ);
			
   // Do some process while the web 
   // service is processing the request
   System.Threading.Thread.Sleep(10000);

}

// CallBack function
private void CallbackSampleMethod(IAsyncResult asyncResult)
{
   // Create an instance of the WebService
   localhost.MyAsyncWebService webServ = 
		(localhost.MyAsyncWebService)asyncResult.AsyncState;
						
   // Get the Result of the WebMethod by calling 
   // the end method of the proxy class
   mLangResult = webServ.EndMyWebMethod(asyncResult);

   // Display the results in a label
   Label1.Text = this.mLangResult;
}

In the Button Click event, the proxy's Begin<WebServiceMethod>(BeginMyWebMethod) web service is called by passing in the following parameters:

The CallBack function should have an instance of the IAsyncResult instance as a parameter (see the above code). After the web service returns the results, then the Callback function is activated (CallbackSampleMethod). Results of the web service are obtained by calling the proxy's End<WebServiceMethod> (EndMyWebMethod).

Asynchronous Call using WaitHandle

This approach utilizes a different technique, in that we have a wait handle object. This object will wait until the web service returns. When the parent thread gets to the function WaitOne (or WaitAll or WaitOne), it waits for the web service to return. After the web service returns the results, then the parent thread process will continue.

string mLangInput = "English";

// Create an instance of the WebService
localhost.MyAsyncWebService webServ =  
new localhost.MyAsyncWebService();

// Create an IAsyncResult object to hold results
IAsyncResult asyncResult;


// Make an Asynchronous Call
asyncResult = webServ.BeginMyWebMethod
  (this.mLangInput, null, null);

// Do something while the WebService is doing its work
str1 = "Doing some work while the WebService is being called.";

// Call WaitHandle to wait for the web service method to return
WaitHandle wtHandle = asyncResult.AsyncWaitHandle;
wtHandle.WaitOne();

// Get the Result of the WebMethod (this occurs when 
// WebService finished processing)
mLangResult = webServ.EndMyWebMethod(asyncResult);

// Display the results in a label
Label1.Text = str1 + this.mLangResult;

This process uses the Begin<WebServiceMethod> (BeginMyWebMethod) and assigns the result to the IAsyncResult instance, passing in method parameters and null values for Callback and asyncState (as callback is not used). The WaitOne() method causes the thread to wait for the results from the web service. When the results from the web service are ready, then End<WebServiceMethod> (EndMyWebMethod) is called to retrieve the results.

Asynchronous Call (Fire and Forget)

Very often, there can arise certain situations where a web method does not return any value. Also, a process may need to be kicked off the web server without waiting for the results. We can better illustrate this with the help of the following example. We will create an XML file using the CreateXmlFile web method, passing in a value. Note: if there is any error while creating an XML file at the web service end, then the web application cannot be notified, as the results are not returned to the web app from the web service.

// WebService web method
using System.Web.Services.Protocols;

[SoapDocumentMethod(OneWay=true)]
[WebMethod()]
public void CreateXMLFile(string lang) //No return value
{
  //. . . . . . . . . . . 
}

Client Call:

string mLangsInput = "English";

// Create an instance of the WebService
localhost.MyAsyncWebService webServ = 
	new localhost.MyAsyncWebService();

// Make a Web Method Call to create an XMLFile
webServ.CreateXMLFile(mLangInput);

// Do some process while the web service is processing the request
System.Threading.Thread.Sleep(10000);

To support fire-and-forget method calls, web methods should have an attribute SoapDocumentMethod, with the OneWay attribute set to true. SoapDocumentMethod resides in the System.Web.Services.Protocols namespace; make sure that this namespace was used in the web service. Fire and forget methods cannot have any return values, as the client does not expect any results. When you use fire and forget web methods, it does not really matter whether you have chosen an asynchronous or synchronous process, as the parent thread does not wait for the results.

WaitHandle Object Methods

The WaitHandle object has three important methods; WaitOne, WaitAll, and WaitAny. Multiple calls can be made asynchronously to web services from a single parent thread. Here is an example:

// Declare two instances of Web Service
localhost.MyAsyncWebService webServ1 = 
	new localhost.MyAsyncWebService();
localhost.MyAsyncWebService webServ2 = 
	new localhost.MyAsyncWebService();

// Declare two instances of IAsyncResult
IAsyncResult asyncResult1;
IAsyncResult asyncResult2;

// Make  two Asynchronous Calls to Web Service
asyncResult1 = webServ1.BeginCreateXMLFile
	(this.mLangInput, null, null);
asyncResult2 = webServ2.BeginCreateXMLFile
	(this.mLangInput, null, null);

// Call WaitHandle to wait for the web service method to return
WaitHandle wtHandle = asyncResult.AsyncWaitHandle;
// Causes to Wait until both the calls are returned
wtHandle.WaitAll();  

// Causes to Wait until any of the calls to be returned
// wtHandleAny();

Here, two calls were made asynchronously to web services. The WaitAll() method causes the parent thread to wait for both (All) of the asynchronous calls to return before it can proceed. The WaitAny() method causes it to just wait for any (Any) asynchronous call to return. If only one asynchronous call is made, then WaitOne() is used. For more information on the WaitHandle object, look here at MSDN.

Callback Vs. WaitHandle

The choice of which approach to use is application-driven. If the application needs the results from the web service to be used in the later stages of the thread, then the WaitHandle is the best approach. For example, if the web service queries a database and retrieves a value that needs to be used in the parent process and then displays the result to the user, then WaitHandle should be used. We need the parent thread to stop processing at a certain stage so that we can use the results from the web service to do further computation and display the final results. Most web apps come under this scenario. In all other scenarios, we can use callback. Mostly Windows apps use this approach.

Conclusion

Asynchronous calls to web services will significantly enhance performance because they enable parallel processing of the parent thread and the web service call by using two separate threads. This will be very effective, especially if the web service method takes a long time to process, since it lets the parent thread continue while the web service is doing its own process.

Related Links

Raj Makkapati is a .NET application developer currently working in Charlotte, North Carolina, developing advanced .NET applications for the mortgage industry.


Return to OnDotNet.com

Copyright © 2009 O'Reilly Media, Inc.