Introduction

Introduction of XML WebServices has redefined the way software components will be distributed all over, and have their functionality consumed by the clients. In fact, with their base being on open standard protocols like SOAP and HTTP, WebServices are poised to become one of the chief means for consuming functionality and provide services across heterogeneous systems. This seems to be quite similar to the effect that HTML brought about.

However, in this article, I shall be focusing on how to communicate asynchronously with WebServices, with specifics on how to go about accomplishing this using the Microsoft .NET framework. Hence, a familiarity with the .NET framework and C#, since the source code will be written in that, will do you a world of good. That said, let's get started.

Introducing WSPrime

I have always been a firm believer in using code to visualize and understand concepts, and this article is no different. Here, we have a webservice that shall return a count of number of prime numbers found till a specified number. For example, if 10 is passed as the argument, then the webservice shall return 4 since there are four prime numbers till 10, namely 2,3,5 and 7. Here's the webservice source code:

The webservice exposes just one method, Prime, that takes an integer as an input and counts the number of prime numbers till the specified number. Once that is done, the count is returned to the caller. The logic for counting primes and checking a number for prime-ness is relatively straight forward.

I installed this webservice in a virtual folder, wstester, on my local machine, and can call it using a browser as http://localhost/wstester/wstester.asmx, where wstester.asmx is the webservice source file. Next, I create its proxy using the WSDL utility that comes with the .NET SDK installation, as follows:

wsdl /l:cs http://localhost/wstester/wstester.asmx

which produces the wsprime.cs file (named after the contained class). For those who doesn't know, the /l parameter is used to specify the language in which the proxy source code will be written, and cs specifies the use of C#. Now comes the meat of the article: having a look inside the proxy!

Inside WSPrime.cs

If you open up the proxy file that was created, here's what it would look like:

//----------------------------------------------------------------------------
// <autogenerated>
// This code was generated by a tool.
// Runtime Version: 1.0.3705.0
//// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
//----------------------------------------------------------------------------
//// This source code was auto-generated by wsdl, Version=1.0.3705.0.
//using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;
///<spanclass="code-SummaryComment"><remarks/></span>

The constructor initializes the URL property to point to the location of the webservice. However, the focus of our attention are the three versions of the Prime method:

The first version, with the name Prime, is a synchronous call to the actual webservice method, and shall block until the webservice returns.

The second version is the asynchronous one, and is comprised of the BeginPrime and EndPrime methods.

Actually, for each exposed method in a webservice, the WSDL utility creates these two versions of the method. The asynchronous version is comprised of two methods each, with Begin and End prefixed to the method name. Usually, developers overlook the asynchronous version of the method, and use the synchronous one, after instantiating the webservice proxy class. Using the asynchronous version requires a little more effort on the part of the developer, but can release the caller from getting blocked when the webservice method is called. Once the call is placed using the Begin method of the proxy, the Begin method sends the request to the webservice and then immediately returns to the caller, which lets the caller do any other work. Once the webservice method is done with its work, it calls back the caller of the Begin method.

So, the question that will be in your mind now is, "How will the webservice notify the caller?". Well, if you notice carefully in the example proxy source code above, the BeginPrime method takes three parameters instead of one, which the webservice method actually takes. The second last parameter, System.AsyncCallback callback, is a delegate object to which the webservice will notify, and hence callback, when it is done. The last parameter, object asyncState, is any state information that you may wish to send to the delegate for its working, when the webservice calls back.

Now that we have some basics clear, let's have a look at an implementation of an asynchronous client for the above examplified webservice:

using System;
using System.Runtime.Remoting.Messaging;
publicclass TestProxy
{
privatestaticbool bEnd=false;
// our delegate which will be called back to notify that work is done.
publicstaticvoid PrimeDoneCallback(IAsyncResult arResult)
{
// get the "this" pointer that was passed...
WSPrime wsp=(WSPrime)arResult.AsyncState;
// call "EndPrime" and get the result..
int iCount=wsp.EndPrime(arResult);
/// show the result of the webservice call..
Console.WriteLine("Async. WebService call found {0} primes.", iCount);
// set flag so that application can terminate...
bEnd=true;
}
publicstaticvoid Main()
{
// take inputs from the user...
int iNum, iCount;
Console.WriteLine("Enter a number till which primes have to be counted: ");
iNum=Convert.ToInt32(Console.ReadLine());
if (iNum<2)
{
Console.WriteLine("Number should be >=2.");
return;
}
// instantiate the webservice proxy class
WSPrime wsp = new WSPrime();
// tell the user what we are doing...
Console.WriteLine("Calling WebService Asynchronously...\n");
// do the async. call... pass it a "WSPrime" object as a "this" object
AsyncCallback acb = new AsyncCallback(TestProxy.PrimeDoneCallback);
wsp.BeginPrime(iNum,acb,wsp);
// so, while the async. call is on way.. lets calculate it on our own
// as well...
Console.WriteLine("Calculating number of primes myself as well...\n");
iCount=0;
for(int i=2;i<=iNum;i++)
{
bool bPrime=true;
for(int j=2;j<i;j++)
{
if (i%j==0)
{
// this isn't a prime number...
bPrime=false;
break;
}
}
// increment count if prime number..
if (bPrime==true)
iCount++;
}
// tell our count to user...
Console.WriteLine("I found {0} primes",iCount);
// wait for webservice to end...
Console.WriteLine("Waiting for webservice to end..");
while(bEnd==false);
Console.WriteLine("Over!");
}
};

Let's walk this source code logically, in an application flow manner. First, the number till which prime numbers have to be counted is taken as an input, and ensured that it is greater than 1. Next, wsp, an object of WSPrime class, is instantiated which, if you remember from the source code above, is the webservice proxy class.

Next, since we have to call the webservice method asynchronously, and hence have to use BeginPrime, we create an AsyncCallback delegate object (hence, we specify using System.Runtime.Remoting.Messaging since this namespace contains the AsyncCallback class) and pass it the address of the method to be called back, namely the static PrimeDoneCallback method of the TestProxy class, which is our main application class. The delegate is defined as a static method of the class, but could have been easily changed to an instance method.

Finally, the BeginPrime method is called on the wsp object and the number, till which prime numbers have to be counted, is passed as the first argument, followed by the callback delegate object acb, and a pointer to the proxy class object. Why have we passed the pointer to the proxy class object? Well, it so happens that whenever we have to call a webservice method asynchronously, we have to do it in pairs of the Begin/End methods. Since the BeginPrime method is called within Main, and control will return to our callback method when the webservice method has finished its work, we need to have, in the callback method, the proxy class object against which the EndPrime method can be called, and obtain the result of webservice method invocation. Thus, the proxy class object, wsp, is passed as the last parameter to BeginPrime.

The BeginPrime method returns immediately, which leaves the caller (i.e. Main) to do other tasks. So, in the sample client above, I have programmed Main to calculate the number of prime numbers till the number specified by the user. Of course, you can do anything here, but I chose to do this so that the output could show results for comparison. Once the work is done, output is shown to the user, and the application is made to block on a static variable of our application class so that our application doesn't exits before the webservice sends the callback.

When the webservice sends the callback, PrimeDoneCallback method is called. This method receives only one argument, which is of the type IAsyncResult. We extract the object pointer to the proxy class object, that was sent as an argument to the BeginPrime method call, from the AsyncState property, and appropriately box it. This is followed by the call to EndPrime method on the proxy class object, passing it the IAsyncResult argument which the delegate method received. The EndPrime method returns the result of the webservice call, which is then displayed as the output, and the flag variable, bEnd is set to true so that the Main method, which is blocked on the flag, gets released and the application can exit.

Finally

To compile the proxy source code, execute the following command at the VS.NET command prompt:

Conclusion

As evident from the exemplification above, by doing a little bit of more effort, a developer can reap the benefits of asynchronous callbacks in webservices, and can make their applications more responsive and scalable. Surprisingly, a good amount of development community doesn't take advantage of this fact, rendering asynchronous webservice communication under-utilized.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.