Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#
Article

Asynchronous Communication with .NET WebServices

Rate me:
Please Sign up or sign in to vote.
4.76/5 (32 votes)
22 Jun 20027 min read 197.3K   868   88   17
Explains how to communicate asynchronously with .NET webservices

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:

C#
<%@ WebService Language="C#" class="WSPrime" %>
using System;
using System.Web.Services;
public class WSPrime : WebService
{
// This method returns the number of prime number lying between
// 2 and num.
[WebMethod]
public int Prime(int num)
{
         int iCount=0;
         for(int i=2;i<num;i++)
         {
                bool bPrime=true;
                for(int j=2;j<i;j++)
                {
                     // is this number prime ?
                     if (i%j==0)
                     {
                          // nope.. it isn't...
                          bPrime=false;
                          break;
                      }
                 }

                 if (bPrime==true)
                     iCount++;
         }

         // return the count..
         return iCount;
}
}

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:

C#
//----------------------------------------------------------------------------
// <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;

/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="WSPrimeSoap", 
                                      Namespace="http://tempuri.org/")]
public class WSPrime : System.Web.Services.Protocols.SoapHttpClientProtocol {

/// <remarks/>
public WSPrime() {
this.Url = "http://localhost/wstester/wstester.asmx";
}

/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
    ("http://tempuri.org/Prime", RequestNamespace="http://tempuri.org/", 
    ResponseNamespace="http://tempuri.org/", 
    Use=System.Web.Services.Description.SoapBindingUse.Literal, 
    ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public int Prime(int num) {
object[] results = this.Invoke("Prime", new object[] {
num});
return ((int)(results[0]));
}

/// <remarks/>
public System.IAsyncResult BeginPrime(int num, 
    System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("Prime", new object[] {
num}, callback, asyncState);
}

/// <remarks/>
public int EndPrime(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((int)(results[0]));
}
}

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:

C#
using System;
using System.Runtime.Remoting.Messaging;
public class TestProxy
{
private static bool bEnd=false;

// our delegate which will be called back to notify that work is done.
public static void 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;
}

public static void 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:

csc /t:library wsprime.cs

Next, compile the webservice client, by referencing the webservice proxy assembly, as shown below:

csc /r:wsprime.dll wsprime_client.cs

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.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I hold Early Acheiver in MCSE 2000, MCSE NT 4.0, MCP+I, and actively involved in programming using C/C++, .NET framework, C#, Win32 API, VB, ASP and MFC.

I also have various publications to my credit at MSDN Online Peer Journal, Windows Developer Journal (http://www.wdj.com/), Developer 2.0 (http://www.developer2.com/), and PC Quest (http://www.pcquest.com/).

Comments and Discussions

 
GeneralGreat Job On This One Pin
Member 287245615-Jul-09 7:05
Member 287245615-Jul-09 7:05 
Questionwhat is your point? Pin
CodeMonkey46198230-Aug-07 11:18
CodeMonkey46198230-Aug-07 11:18 
GeneralSystem.Runtime.Remoting.Messaging not required Pin
aftabbaig1-Dec-06 0:13
aftabbaig1-Dec-06 0:13 
You've mentioned in your document that AsyncCallBack class is contained in System.Runtime.Remoting.Messaging. This is not true.

Infact the AsyncCallBack class is under the System namespace.
The System.Runtime.Remoting.Messaging namespace has a class AsyncResult which is not needed in this project (we r using the interface IAsyncResult in this project which is also contained in the System namespace.

Thanks,
Aftab Baig

aFtab bAig

GeneralSome remarks Pin
Zhekov11-Jul-06 1:42
Zhekov11-Jul-06 1:42 
Generalcallback function does not seem to work Pin
arsoni27-Jan-06 11:06
arsoni27-Jan-06 11:06 
GeneralGood Article Pin
vijay_jvkn26-Dec-05 22:23
vijay_jvkn26-Dec-05 22:23 
GeneralWeb Services Synchronized or Asynchronized Pin
Anonymous21-Oct-05 1:14
Anonymous21-Oct-05 1:14 
Questionnon-.NET Web services.. Pin
vinod_rv30-Aug-05 5:07
vinod_rv30-Aug-05 5:07 
QuestionHow do I get the reply back into my object? Pin
Peterw721-Apr-04 2:41
Peterw721-Apr-04 2:41 
AnswerRe: How do I get the reply back into my object? Pin
Matt Gerrans12-Sep-05 14:06
Matt Gerrans12-Sep-05 14:06 
QuestionAsync why? Pin
Jacee8-Jan-04 2:30
Jacee8-Jan-04 2:30 
AnswerRe: Async why? Pin
Kinlan8-Jan-04 4:05
Kinlan8-Jan-04 4:05 
GeneralRe: Async why? Pin
Jacee8-Jan-04 7:16
Jacee8-Jan-04 7:16 
GeneralRe: Async why? Pin
Kinlan10-Jan-04 1:29
Kinlan10-Jan-04 1:29 
GeneralRe: Async why? Pin
dzzxyz12-Mar-04 12:17
dzzxyz12-Mar-04 12:17 
GeneralRe: Async why? Pin
_Guanche_8-Nov-07 10:03
_Guanche_8-Nov-07 10:03 
GeneralGood example ... Pin
Makulik9-Jun-03 6:52
Makulik9-Jun-03 6:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.