• home
  • forum
  • my
  • kt
  • download
  • ASP.NET Web Services : Asynchronous Programming

    Author: 2007-07-03 11:25:08 From:

    To wait or not to wait; that is the question! Whether or not to implement asynchronous processing is one of the fundamental issues that a developer must answer when invoking function calls across process boundaries. Given that the option to invoke an asynchronous call is available, the programmer has to weigh the relative ease of coding synchronous calls with its inherent drawback - when a synchronous call is made, the calling thread is blocked and has to wait until the function completes. In many instances, this is an acceptable shortcoming, as in the case when a program's logic flow should not continue until data is retrieved from a database. Asynchronous processing, on the other hand, allows more parallelism. A thread that initiates an asynchronous call is not blocked and can therefore do almost any computation while the method is in transit. The case for asynchronous processing becomes very compelling in the enterprise computing space where systems need to handle hundreds of thousands of function call requests and synchronicity may become a barrier to scalability.

    Web Services support both synchronous and asynchronous communication between the client application and the server hosting the service. Since both are supported, the developer is challenged with deciding which type of process to initiate in their application. In this chapter, we're going to dive into the subject of synchronous and asynchronous programming as it pertains to ASP.NET Web Services. We'll explore the mechanics of invoking Web Services via an asynchronous mechanism. We'll also look into the .NET framework and how it provides the infrastructure for asynchronous processing.

    So, the topics for this chapter are:

    • Synchronous versus asynchronous invocations
    • Asynchronous design patterns in .NET
    • How to invoke Web Services asynchronously
    • What to consider when developing asynchronous Web Service calls

    Synchronous Versus Asynchronous Invocations :
    As we learned in Chapter 2, the .NET framework shields the programmer from the complexities of generating a remote procedure call (RPC) to a Web Service hosted on another server. As far as we are concerned, they are simply making a method call to another component. The mechanism for invoking the method call looks the same whether the server component is within the application's assembly or many miles away running on another machine.

    Despite their similarities on the surface, the underlying plumbing used to invoke a Web Service is very different from an in-process function call. In the case of a Web Service, the function call is packaged into a SOAP message and marshaled across the Internet via the HTTP protocol. Because of the inherent nature of the Internet, performance of the RPC call to the Web Service can vary greatly from one call to the next. The design choices you make when developing your Web Service client application can make a big difference in how your users will perceive application availability and performance.

    In this section, we'll compare the merits and shortcomings of both the synchronous and asynchronous approaches to calling Web Services. We'll also learn how to actually develop client applications that make use of the asynchronous mechanisms built into the .NET framework. This will set the stage for a deeper discussion of asynchronous processing in the subsequent sections.

    The Case for Synchronous Processing :
    Synchronous operations consist of component or function calls that operate in lockstep. A synchronous call blocks a process until the operation completes. Only then will the next line of execution be invoked. There are many examples in life that model this pattern of behavior. The cafeteria line at your local restaurant, for example, behaves in a synchronous fashion. Customers are serviced one at a time. While they are in line, they are blocked from conducting other activities, and they wait until all their food choices are served before they can continue with their lunch-break. Of course, after waiting for a very long time they might give up and leave. Here's a diagram illustrating the concept of synchronous processes:

    Synchronous Processes
    Synchronous Processes

    As you can see, the procedure for making synchronous calls is straightforward:

    1. The client obtains an interface pointer to the server object and calls a method through that pointer
    2. The client waits until the server either completes the method call, or if there is no response for a given period of time the client raises an error
    3. Only after the method call returns is the client free to continue with its processing

    It is this simplicity that makes synchronous processes a compelling choice. Most of the time, the performance achieved from method calls is acceptable and does not warrant the extra overhead required for concurrent processing. In fact, most of the function calls in the .NET framework are synchronous to minimize problems that can arise from asynchronous message processing. Likewise, the method calls you will be implementing will be done in a synchronous fashion in most cases.

    Asynchronous message passing, on the other hand, is more difficult to code and introduces several problems. What happens if the method call is not delivered to the server object successfully? The calling process does not wait for delivery of the message, and thus never hears about the error. The operating system has to provide the infrastructure for reporting such errors, or worse, the programmer may have to write special code to handle such cases. Another related problem is how will the calling application discover the completion of the called function? The application will either have to create a polling mechanism, event trigger, or callback method in order to be later notified of the operation.

    Because synchronous messaging is so easy to implement, you may be tempted to take the simple route and always use a synchronous mechanism when invoking a Web Service from your client code. Consider your choice carefully because this decision will have an impact on how your client application will perform. When implemented properly, using asynchronous communication may improve system usage and avoid delays on the client side, while waiting for the Web Service results.

    When Asynchronous Processing Is Better :
    When method calls are invoked across process and machine boundaries via an RPC mechanism, it's oftentimes a likely candidate for asynchronous processing. This is definitely true in the case of Web Services where the remote procedure call is sent via HTTP and must deal with issues such as bandwidth constraints and network latency.

    What makes asynchronous method invocations a good choice for communicating with Web Services? An asynchronous operation will not block the calling thread, which only initiates the operation. The calling application must then discover completion of the call by polling, by software interrupt, or by waiting explicitly for completion later. An asynchronous operation will need to return a call or transaction ID if the calling application needs to be later notified about the operation. At notification time, this ID would be placed in some global location or passed as an argument to a handle or wait call. Here is a diagram illustrating the concept of an asynchronous process:

    Asynchronous Process
    Asynchronous Process

    The procedure for making an asynchronous call is not as simple as its synchronous counterpart:

    1. The client obtains an interface pointer to the server object and calls the method asynchronously. The client includes a function pointer to a sink object for message callback.
    2. The call returns immediately and the calling thread is free to execute the next line of code.
    3. When the method is finished processing the request, the server notifies the client through the callback routine in the sink object.

    Even with advancements implemented in the .NET framework, successfully developing asynchronous programming logic is not trivial. You need to examine the requirements of your application carefully to determine whether or not the code you're writing can even benefit from asynchronous events. Here are some general guidelines to consider when making your decision:

    • Consider asynchronous processing if the calling thread controls a Windows user interface. In this case, the calling thread can't afford to be blocked during a remote method call because the UI will freeze.
    • Asynchronous processing may help with scalability if the Web Services client is an ASP.NET application or another ASP.NET Web Service. In this scenario, a blocked synchronous call in the code can stall the ASP.NET worker thread, which can force other applications' requests to queue and, therefore, impact scalability. Using asynchronous communication instead could at least free up the threads that ASP.NET isn't using for the Web Service calls. Asynchronous server processing is discussed in detail towards the end of this chapter.
    • If there is a possibility that the remote procedure call to the Web Service may take a while to complete, asynchronous processing may be beneficial. In this case the client application can do other work on the thread before it needs the results from the remote procedure call.
    • The client application may need to make concurrent calls to one or more remote services. In this case, using an asynchronous remote procedure call is much better than spinning off multiple threads to do the same work. For example, if an application needs to make concurrent synchronous calls to three different Web Services, it cannot do so with one thread. It has to spin off at least two threads and make a remote call in each thread. However, if the client uses an asynchronous remote call, it can make all three calls on one thread and then wait for all of them.

    A Sample Web Service :
    Now that we've discussed the pros and cons of both the synchronous and asynchronous programming methodologies, let's write some code to illustrate the concepts. We'll begin by creating an ASP.NET Web Service that our client application can invoke. We'll then create two separate applications, with one calling the Web Service synchronously, and the other asynchronously, so that we can compare the techniques of each approach.

    For the purposes of our discussion, we will be making use of an ASP.NET Web Service that returns a stock quote. The Web Service, named StockService, accepts a ticker symbol parameter that will return a string representing the value of the stock. In order to properly demonstrate asynchronous programming, the Web Service we will be invoking will also have the ability to simulate a long-running process. For that purpose, the StockService example accepts another parameter that represents the number of seconds the service will wait before returning the stock value back to the calling application. opposite is a screenshot of the StockService.asmx page displaying the GetStockQuote method.

    StockService.asmx
    StockService.asmx

    The TickerSymbol parameter will accept a string value representing a stock symbol or a company name. The DelayInSeconds parameter will accept an integer value representing the number of seconds the Web Service will wait before returning the stock's value. Below is the code for the StockService Web Service written in C#. Bear in mind that this Web Service is simply a simulation for illustrative purposes only and obviously doesn't return accurate stock values. The actual algorithm that a production Web Service would use to return a stock's actual value will be much more complex than this example.

    StockService.asmx :

    <%@ WebService Language="C#" Class="StockWebService.StockService" %>
    
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Web;
    using System.Web.Services;
    using System.Threading;
    
    namespace StockWebService {
    
      public class StockService :  System.Web.Services.WebService
      {
        [WebMethod(Description="Returns  a stock  quote")]
        public string GetStockQuote(string TickerSymbol, int DelayInSeconds)
        {
          //Create a delay to  simulate a long-running  process
          if (DelayInSeconds >  0)
          {
            //Have the thread sleep  based on the DelayInSeconds  parameter
            //Note:  The  constant "1000"  is to convert seconds to milliseconds
            System.Threading.Thread.Sleep(DelayInSeconds * 1000);
          }
    
          //Retrieve the stock quote based on  the  TickerSymbol parameter
          //NOTE:  Stock values are for simulation  purposes only
          string Quote;
          switch (TickerSymbol.ToUpper())
          {
            case "MSFT":
              Quote =  "67";
              break;
            case "SUNW":
              Quote =  "36  31/32";
              break;
            case "IBM":
              Quote =  "80";
              break;
            case "ORCL":
              Quote =  "25  1/32";
              break;
            case "CSCO":
              Quote =  "51";
              break;
            default:
              Quote =  "Unknown";
              break;
          }
          
          //Return value of Quote  to calling application
          return Quote;
        }
      }
    }

    The Web Service has one method, GetStockQuote. GetStockQuote accepts the two parameters we previously discussed: TickerSymbol and DelayInSeconds.

    [WebMethod(Description="Returns a stock quote")]
    public string GetStockQuote(string TickerSymbol, int DelayInSeconds)

    The GetStockQuote method returns a string, which represents the value of the requested stock. The method will return a string value of "Unknown" if the stock's value is not known.

    Additionally, this method has the functionality to delay the delivery of the return message to simulate a long server-side process. The code accomplishes this by retrieving a handle to the current thread and causes the thread to sleep for the duration specified in the DelayInSeconds parameter:

    	//Have the thread sleep based on the DelayInSeconds parameter
    	//Note: The constant "1000" is to convert seconds to milliseconds
    	System.Threading.Thread.Sleep(DelayInSeconds * 1000);

    Here is the output of the GetStockQuote method when invoked from the sample page provided by ASP.NET:

    Stock Service
    Stock Service

    You will notice in our sample Web Service that there is nothing specific in the implementation of StockService that provides asynchronous functionality. It's actually the calling application that will decide if a particular call should be asynchronous. This is in keeping with one of the tenets of the .NET framework, which states that it's not necessary for a called object to do additional programming to support asynchronous behavior by its clients. In fact, the called object doesn't even have to be an ASP.NET Web Service. It could be a Web Service developed on another platform and we would still be able to invoke an asynchronous call from a client running on the .NET framework.

    Using the Sample Web Service :
    Now let's have some fun by invoking this Web Service from a client application both, synchronously and asynchronously. The client will be developed as a console application using Visual Studio .NET. As you learned in Chapter 3, we'll first need to create a SOAP proxy to serve as a wrapper class for the StockService Web Service. You can generate the proxy class using the WSDL.exe utility, which comes with the .NET SDK, or simply add a web reference to the Web Service in the Visual Studio .NET development environment. If you wish to use the WSDL.exe utility, you can generate the proxy class using Build.bat from the StockWebService folder of the download (assuming you're running the StockService Web Service locally in your machine):

    Wsdl /l:vb http://localhost/stockwebservice/stockservice.asmx?WSDL

    The generated C# proxy class contains both synchronous and asynchronous versions of the GetStockQuote method. The asynchronous version consists of two methods, a BeginGetStockQuote and an EndGetStockQuote method. The BeginGetStockQuote method is used to initiate the call to the Web Service, while the EndGetStockQuote method retrieves the results. We'll take a closer look at this proxy class in the "Asynchronous Programming in .NET" section of this chapter.

    A Sample Synchronous Method Call :
    The code for calling a Web Service synchronously should be relatively familiar by now, since many of the examples in this book have been synchronous remote procedure calls to Web Services. This example will be no different. Here's the C# code for the console application, SyncStockClient.exe in the bin subfolder of the SyncStockClient folder in the download, that uses the StockService Web Service:

    using System;
    using System.Runtime.Remoting.Messaging;
    
    namespace SyncStockClient_CSharp
    {
       class SyncStockClient
       {
          static void Main(string[] args)
          {
             string ReturnValue;
             //Create the web service instance via the proxy class
             localhost.StockService objStockService = new localhost.StockService();
             
             //Make sure there are at least two items in args[] array
             //NOTE: the args[] array contains command line arguments
             if (args.GetUpperBound(0) >= 1)
             {
                //Invoke the synchronous call to the web service
                //This thread is blocked until the method call returns
                // with the return value
                //NOTE: System.Convert.ToInt32 converts the string
                // argument to integer
                ReturnValue = objStockService.GetStockQuote(args[0],
    				System.Convert.ToInt32(args[1]));
                
                //Display the results to the user
                Console.WriteLine("Stock quote for " + args[0] + " is " + ReturnValue);
             }
             else
             {
                //User did not enter the right number of parameters
                Console.WriteLine("You need to input 2 parameters. Try again.");
             }
          }
       }
    }

    SyncStockClient.exe is designed to be used at the command prompt. This application accepts two command-line parameters that will then be passed to the GetStockQuote method call of the StockService Web Service. Here's the line of code that invokes the Web Service method:

    	ReturnValue = objStockService.GetStockQuote(args[0],
    	System.Convert.ToInt32(args[1]));

    When the application starts, the command-line is parsed by the GetCommandLineArgs method and the command-line entries are then stored in the args array. When the user starts the application correctly, args[0] will contain the StockSymbol parameter and args[1] will contain the DelayInSeconds parameter. Below is the output for SyncStockClient.exe:

    SyncStockClient.exe
    SyncStockClient.exe

    In this example, the user starts the SyncStockClient.exe application, requesting the stock quote for Microsoft (MSFT) and setting a server-side delay of 5 seconds:

    C:\>SyncStockClient msft 5

    Since the Web Service call, in this example, is synchronous, the application has no choice but to wait for the processing to conclude before running the next line of execution, which then prints the outcome of the method call:

    Stock quote for msft is 67

    Let's now look at a similar application that calls StockService asynchronously.

    A Sample Asynchronous Method Call :
    This next example will also be a console-based application written in Visual Basic .NET. It's identical in functionality to SyncStockClient.exe with the exception that the GetStockQuote method will be invoked asynchronously. Here's the C# code for the console application, AsyncStockClient.exe, that uses the StockService Web Service:

    using System;
    using System.Runtime.Remoting.Messaging;
    
    namespace AsyncStockClient_CSharp
    {
       class AsyncStockClient
       {
          static void Main(string[] args)
          {
             string ReturnValue;
             IAsyncResult AsyncResult;
             
             //Create the web service instance via the proxy class
             localhost.StockService objStockService = new localhost.StockService();
             
             //Make sure there are at least two items in the args[] array
             //NOTE: the args[] array contains command line arguments
             if (args.GetUpperBound(0) >= 1)
             {
                //Invoke the asynchronous call to the Web Service
                //NOTE: System.Convert.ToInt32 converts the string argument
                // to an integer
                AsyncResult = objStockService.BeginGetStockQuote(args[0],
                   System.Convert.ToInt32(args[1]), null, null);
                
                //Method call returns right away!
                //This thread is free to do more processing
                //Check for method completion in the while loop
                Console.Write("I'm not blocked. I can do more processing");
                
                while(AsyncResult.IsCompleted == false)
                {
                   Console.Write(".");
                }
                
                Console.WriteLine("Method call has returned!");
                //Retrieve return value from the Web Service
                ReturnValue = objStockService.EndGetStockQuote(AsyncResult);
                
                //Display the results to the user
                Console.WriteLine("Stock quote for " + args[0] + " is " + ReturnValue);
             }
             else
             {
                //User did not enter the right number of parameters
                Console.WriteLine("You need to input 2 parameters. Try again.");
             }
          }
       }
    }

    The call to BeginGetStockQuote starts the asynchronous communication process, sending out the method request and then returning immediately. The return value of this method call is not yet the actual stock quote, yet, as was the case in the synchronous version. Instead, it's an object of type IAsyncResult, which is part of the System.Runtime.Remoting.Messaging namespace. The AsyncResult object will be used later to poll for completion and to fetch the results of the method call:

    	//Invoke the asynchronous call to the Web Service
    	//NOTE: System.Convert.ToInt32 converts the string argument to integer
    	AsyncResult = objStockService.BeginGetStockQuote(args[0],
    System.Convert.ToInt32(args[1]), null, null);

    The parameter list of the BeginGetStockQuote begins with the parameters of the synchronous method - StockSymbol and DelayInSeconds. The method has two additional parameters used in providing a callback mechanism for the asynchronous process. Since we won't be using a callback mechanism in this example, the C# code simply passes null to the last two parameters (we'll be covering the callback mechanism in a later example in this chapter).

    Once the Web Service method has been invoked asynchronously, the calling thread needs a way to find out when the operation has completed. The IAsyncResult class contains a property for just this purpose, called IsCompleted, which will return the Boolean value, true, when the Web Service is finished processing the request. In the code, outlined below, we call the AsyncResult.IsCompleted method periodically to check for method call completion.

    	//Method call returns right away!
    	//This thread is free to do more processing
    	//Check for method completion in the while loop
    	Console.Write("I'm not blocked. I can do more processing");
    	
    	while(AsyncResult.IsCompleted == false)
    	{
    		Console.Write(".");
    	}

    To retrieve the results of the operation, we call the EndGetStockQuote method provided by the Web Service's proxy class:

    	//Retrieve return value from the Web Service
    	ReturnValue = objStockService.EndGetStockQuote(AsyncResult);

    The method accepts one parameter of type IAsyncResult. In this case, we pass the AsyncResult object that we originally received from the BeginGetStockQuote method. This is how the .NET infrastructure is able to determine which result to give back to our code, since the client may have invoked any number of requests at the same time.

    Note that we need to wait for the AsyncResult.IsCompleted to return true before invoking the EndGetStockQuote method. If we call the EndGetStockQuote method before the operation is finished, it will block until the operation does in fact complete. Below is the output for AsyncStockClient.exe:

    AsyncStockClient.exe
    AsyncStockClient.exe

    In this example, the user starts the AsyncStockClient application, requesting the stock quote for Microsoft (MSFT) and setting a server-side delay of 5 seconds:

    	C:\>AsyncStockClient msft 5

    Since the Web Service call in this example, is asynchronous, the method call returns immediately and the calling thread is free to do more processing. In this case, the calling thread writes to the console to demonstrate that it's able to do more work:

    	I'm not blocked. I can do more processing...............

    At the same time, the client application is polling to check if the method call has returned. Upon completion of the call request, the client then prints the outcome of the method call:

    	Method call has returned! Stock quote for msft is 67

    Asynchronous Programming in .NET :
    We've now gotten our feet wet with a relatively simple example of an asynchronous method call to a Web Service. Let's dig deeper into the plumbing of the .NET framework to find out how the common language runtime provides the infrastructure for asynchronous programming.

    Until the .NET framework, coding against an asynchronous model had always been a daunting task: one had to be intimately familiar with the complexities of multi-threaded programming in order to create an asynchronous process. For programmers, synchronous method calls are a no-brainer: a single thread initiates a task, and then does nothing until the task completes. Providing a mechanism to free up the main thread, however, required more complex programming involving spawning worker threads to initiate the function call, thread synchronization, and providing a callback mechanism to alert the main thread that the function call has completed. Thankfully, the .NET framework provides an infrastructure to address many of these issues.

    The .NET Runtime Provides the Plumbing :
    Having programmed in Visual Basic for most of my professional career, I used to be envious of my C++ counterparts and their seemingly magical ability to create multi-threaded applications that did any number of parallel tasks, which is a requirement for asynchronous processing. Sure, I was able to mimic asynchronous behavior in VB using out-of-process servers and the use of the dreaded timer control. It worked, but it just wasn't elegant. And the application definitely didn't scale.

    Life became easier with the advent of VB 6 and its ability to use apartment-model threading. In addition, the programmer's arsenal of tools increased with Windows 2000 providing more ways to make asynchronous calls using COM+ events, and COM asynchronous interfaces.

    Although it's still a non-trivial undertaking, the .NET framework makes programming asynchronous processes much easier than before. .NET has asynchronous code built in, and asynchronous programming is a feature supported by many areas of the .NET framework, including I/O operations, networking, messaging, message queues, Async delegates, ASP.NET web forms and, of course, Web Services.

    A Common Design Pattern :
    In order to provide a consistent framework for modeling asynchronous processes, the .NET framework provides an important common design pattern as a core guiding concept. The basic ideas behind this pattern are as follows:

    • The .NET framework will provide services needed for supporting the asynchronous programming model.
    • It is the client code that decides if a particular call should be asynchronous.
    • It is not necessary for a called object to do additional programming to support asynchronous behavior by its clients. The common language runtime infrastructure should be able to handle the difference between the caller and called object views.
    • The called object can choose to explicitly support asynchronous behavior, either because it can implement it more efficiently than a general architecture, or it wants to support only asynchronous behavior by its callers. However, it is recommended that such called objects follow the asynchronous design pattern for exposing asynchronous operations.
    • The common language runtime provides type safety. For Async delegates, which are explained in the next section, the compiler generates type-safe method signatures for the BeginInvoke and EndInvoke method calls.

    Because all the .NET languages make use of the common language runtime, the programmer is not constrained to using a specific programming language to provide asynchronous processing within the .NET framework. The examples demonstrating asynchronous processing provided in this chapter could have easily been written in VB.NET as well as C#.

    Let's now examine one of the key concepts in the .NET framework that makes asynchronous programming possible: the .NET delegate classes.

    An Introduction To Delegates :
    As previously mentioned, one of the requirements of asynchronous programming is that there needs to be a way for a calling thread to find out when an operation has completed processing. The example outlined earlier provided this functionality through a polling mechanism. Another technique is to provide a sink object with a callback method to allow the called object to inform the client that processing is complete.

    In the C/C++ languages, the address of a function, known as a function pointer, is used to provide the callback functionality. VB6 had a similar technique using the AddressOf operator, although its use was limited to returning the address of procedures in a BAS module. The problem with function pointers is that they are just memory addresses. Memory addresses don't contain any additional information about the function they are referring to, such as the number of parameters expected, the types of the parameters, and the return value type. Also, because function pointers cannot be differentiated from other pointer types, you can essentially point them to anything. In other words, function pointers are not type-safe, and this is often the source of many errors in C++ programs.

    The .NET framework provides the same callback functionality that a C++ function pointer provides through the mechanism of delegates. Unlike the function pointer, however, delegates are type-safe. Since one of the goals of the .NET framework is to be type-safe, all types in the .NET framework are self-describing, including delegates. A delegate can, therefore, be thought of as a type-safe, self-describing function pointer object.

    Using Delegates :
    .NET delegate classes behave as containers. A delegate contains the information about a single method and is used to provide a communication mechanism between the server object and the client application. Here's a sample delegate declaration in C#:

    public delegate int MyDelegate(ParameterValue As String);

    When a delegate is created in code, the compiler creates a class that, in addition to a method called Invoke, which is used to initiate a synchronous connection to the methods referred to by the delegate, also creates methods called BeginInvoke and EndInvoke.

    BeginInvoke :
    BeginInvoke starts execution of your asynchronous operation on a different thread from the calling method and accepts the parameters specified in the delegate, plus a few others:

    • An AsyncCallback delegate, which allows you to pass a callback method to be called as soon as the asynchronous method is done.
    • An AsyncState object representing some context information for the callback.

    The return value of the BeginInvoke method is a reference to an object that implements the IAsyncResult interface. The IAsyncResult object returned from the delegate's BeginInvoke method can be cast to an AsyncResult object. The AsyncResult object has the AsyncDelegate property that is the delegate object on which the asynchronous call was invoked.

    EndInvoke :
    EndInvoke is used to harvest the results of the method call. The AsyncResult object returned by BeginInvoke is passed to EndInvoke as a parameter to provide access to the remoting infrastructure that was used to make the asynchronous call. It returns the value (and output/reference parameters) that the asynchronously invoked method returned. EndInvoke will also block the calling thread, if called early, until the asynchronously executing method returns, and then returns the results to you.

    Using Asynchronous Calls in .NET :
    The .NET framework's scheme for asynchronous processing is relatively simple, but powerful. The beauty of the design is that any method can be called in this manner. All that needs to be done is to define an appropriate delegate and call that delegate's BeginInvoke and EndInvoke methods to communicate asynchronously. You don't have to write complex code that spawns threads, nor do you have to worry about how parameters are passed or how results are returned. The .NET infrastructure takes care of the heavy lifting for you.

    Invoking Web Services Asynchronously :
    Invoking Web Services asynchronously from within a .NET client application follows the same design pattern used by the .NET framework for invoking asynchronous processes. The design pattern dictates that, for each synchronous method implemented, there should be two asynchronous methods: a Begin and an End asynchronous method. The Begin method takes input from the client and kicks off the asynchronous operation. The End method supplies the results of the asynchronous operation back to the client.

    In addition to accepting the input parameters required by the asynchronous operation, the Begin method also takes an AsyncCallback delegate to be called when the asynchronous operation is completed. The AsyncCallback delegate will serve as a pointer to a function that the client application will implement to retrieve the results from the method call. The return value of the Begin method is a waitable object that implements the IAsyncResult interface used by the client to determine the status of the asynchronous operation. The client application will then use the End method to obtain the results of the asynchronous operation by supplying the AsyncResult object.

    When calling the Begin method to kick-off an asynchronous call, there are two options available to the client for initiating the operation:

    • Supply the AsyncCallback delegate when beginning the asynchronous operation. This will provide a mechanism for the server to notify the client application that the method call has completed.
    • Don't supply the AsyncCallback delegate when beginning the asynchronous operation. The callback delegate is not required if the client application chooses to poll for completion instead, or if the return value of the function being called is not needed.

    The client application also has a number of options available for completing asynchronous operations:

    • Poll the returned IAsyncResult.IsCompleted property periodically for completion. Note that this does add processing overhead due to the constant polling.
    • Attempt to complete the operation prematurely by calling the End method, which blocks the calling thread until the operation completes.
    • Wait on the IAsyncResult object. The difference between this and the previous option is that the client can use timeouts to wake up periodically.
    • Wait for the callback to occur and complete the operation inside the AsyncCallback routine.

    The asynchronous programming example we studied earlier did not use a callback mechanism, but simply polled the IAsyncResult.IsCompleted property to check for method completion. Before we dive into our next example, which makes use of the callback mechanism let's take another look at the Web Services proxy class to fully understand how Web Services are invoked asynchronously.

    A Closer Look At the Web Services Proxy Class :
    The Web Services proxy class, built by the WSDL.exe tool, provides a wrapper around the .NET remoting functionality required by our client application to communicate with a remote Web Service. The proxy class for the StockService Web Service, which we built earlier, inherits from the SoapHttpClientProtocol class. The SoapHttpClientProtocol class is ideal for asynchronous calling and, therefore, has the BeginInvoke and EndInvoke methods built into its structure. Below is the code for the proxy class, StockService.cs:

    namespace AsyncStockClient_CSharp.localhost
    {
       using System.Diagnostics;
       using System.Xml.Serialization;
       using System;
       using System.Web.Services.Protocols;
       using System.Web.Services;
       
       [System.Web.Services.WebServiceBindingAttribute(Name="StockServiceSoap",
          Namespace="http://tempuri.org/")]
       public class StockService : System.Web.Services.Protocols.SoapHttpClientProtocol
       {
          public StockService()
          {
             this.Url = "http://localhost/stockwebservice/stockservice.asmx";
          }
          
          [System.Web.Services.Protocols.SoapDocumentMethodAttribute(
             "http://tempuri.org/GetStockQuote",
             Use=System.Web.Services.Description.SoapBindingUse.Literal,
             ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
          public string GetStockQuote(string TickerSymbol, int DelayInSeconds)
          {
             object[] results = this.Invoke("GetStockQuote", new object[] {
                TickerSymbol, DelayInSeconds});
             return ((string)(results[0]));
          }
          
          public System.IAsyncResult BeginGetStockQuote(string TickerSymbol,
             int DelayInSeconds, System.AsyncCallback callback, object asyncState)
          {
             return this.BeginInvoke("GetStockQuote", new object[] {
                TickerSymbol, DelayInSeconds}, callback, asyncState);
          }
             
          public string EndGetStockQuote(System.IAsyncResult asyncResult)
          {
             object[] results = this.EndInvoke(asyncResult);
             return ((string)(results[0]));
          }
       }
    }

    The Web Service proxy class encapsulates the mechanism for calling a Web Service asynchronously. When the proxy class is auto-generated by Visual Studio .NET, there are three methods created for each public Web Service method in the Web Service. The table below describes these three methods:

    Web Service Proxy Methods
    Web Service Proxy Methods

    The Begin<NameOfWebServiceMethod> method in the proxy class is simply a wrapper around the BeginInvoke method call that we discussed earlier. The same is true for the End<NameOfWebServiceMethod> in the case of the EndInvoke method.

    An Asynchronous Method Call Using a Callback :
    Now that we've covered the mechanics of asynchronous processing from within the .NET framework, we should have enough information to develop another sample application that will make use of a more sophisticated asynchronous mechanism, the use of a callback delegate.

    This next example will be a Windows Forms-based application written in C#. The application will have code to invoke our StockService Web Service in both a synchronous and an asynchronous fashion.

    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.Runtime.Remoting.Messaging;
    
    namespace StockServiceClient
    {
       public class frmMain : System.Windows.Forms.Form
       {
          // Create an instance of the Web Service via the proxy class
          localhost.StockService objWebService = new localhost.StockService();
          
          private void btnCallSync_Click(object sender, System.EventArgs e)
          {
             string ReturnValue;
    
             //Display hourglass to show system is busy
             this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
    
             //Call the Web Service synchronously and wait for a response
             ReturnValue = objWebService.GetStockQuote(txtSymbol.Text,
                System.Convert.ToInt32(txtDelay.Text));
             
             //Display stock quote to user
             lblQuote.Text = "Stock Quote = " + ReturnValue;
             
             //Return cursor to default pointer
             this.Cursor = System.Windows.Forms.Cursors.Default;
          }
          
          private void btnCallAsync_Click(object sender, System.EventArgs e)
          {
             //Create an instance of a CallBack delegate and
             //point the object to our callback function
             AsyncCallback AsyncCallback = new AsyncCallback(MyCallBack);
             
             //Call the Web Service asynchronously
             //Display a message box to notify a successful call
             IAsyncResult AsyncResult;
             AsyncResult = objWebService.BeginGetStockQuote(txtSymbol.Text,
                System.Convert.ToInt32(txtDelay.Text), AsyncCallback, null);
             MessageBox.Show("The asynchronous was invoked successfully.",
                "Asynchronous Call");
          }
          
          private void MyCallBack(System.IAsyncResult AsyncResult)
          {
             string ReturnValue;
    
             //End asynchronous call and retrieve the return value
             //Display a message box to notify a successful return
             ReturnValue = objWebService.EndGetStockQuote(AsyncResult);
             MessageBox.Show("Asynchronous response has returned with the value.",
                "Asynchronous Call");
             lblQuote.Text = "Stock Quote = " + ReturnValue;
          }
       }
    }

    This Windows application consists of one form containing two textboxes to provide a way for the user to input the parameters required by the StockService Web Service: StockSymbol and DelayInSeconds. The form also includes two buttons: one to invoke StockService synchronously, and the other for asynchronous operation. Here's a screenshot of the sample application:

    Screenshot of the sample application
    Screenshot of the sample application

    The code for synchronous invocation is essentially the same as the previous example. We will therefore skip this section of the code and jump right into the asynchronous implementation code.

    The event handler for the Asynchronous Call button contains the code that initiates the asynchronous call. The code begins with the creation of the AsyncCallback delegate object. This will serve as the pointer to the callback function named MyCallBack:

    	//Create an instance of a CallBack delegate and
    	//point the object to our callback function
    	AsyncCallback AsyncCallback = new AsyncCallback(MyCallBack);

    As we saw earlier in our sample of an asynchronous method call, the call to BeginGetStockQuote starts the asynchronous communication process, sending out the method request, and then returning immediately. The return value of this method call is not yet the actual stock quote, yet, as was the case in the synchronous version. Instead, it's an object of type IAsyncResult, which is part of the System.Runtime.Remoting.Messaging namespace. The AsyncResult object will be used later to fetch the results of the method call:

    	IAsyncResult AsyncResult;
    	AsyncResult = objWebService.BeginGetStockQuote(txtSymbol.Text,
    		System.Convert.ToInt32(txtDelay.Text), AsyncCallback, null);

    The parameter list of the BeginGetStockQuote method begins with the parameters required by StockService - StockSymbol and DelayInSeconds. The method has two additional parameters used in providing a callback mechanism for the asynchronous process:

    Web Service Methods
    Web Service Methods

    Once the Web Service method has been invoked asynchronously, the call, of course, returns straightaway. This is demonstrated by the fact that the Windows Forms application is not blocked and is responsive to user interaction. In this example, the client will not be doing any polling to check for completion. Since the callback delegate was provided to the marshaling infrastructure in the BeginGetStockQuote method, the calling thread will be notified that the method is finished through the callback function outlined below:

    	private void MyCallBack(System.IAsyncResult AsyncResult)

    To retrieve the results of the operation, we call the EndGetStockQuote method provided by the Web Services proxy class from within the callback function:

    	//End asynchronous call and retrieve the return value
    	//Display a message box to notify a successful return
    	ReturnValue = objWebService.EndGetStockQuote(AsyncResult);

    The method accepts one parameter of type IAsyncResult. In this case, we pass the AsyncResult object that we originally received from the BeginGetStockQuote method. This is how the .NET infrastructure is able to determine which result to give back to our code, since the client may have invoked any number of requests at the same time.

    Design Considerations :
    This section will cover additional topics about synchronous and asynchronous processing that you may wish to consider when developing your server-side Web Service or client application.

    Handling Timeouts :
    If you decide to use the synchronous Web Service invocation model in your client application, you have to consider how your application will behave if the Web Service is not performing optimally. For example, the communications pipe your client is using may be bandwidth constrained, or the Web Service could become saturated with client requests, causing server response times to degrade significantly.

    In cases like these, you may decide to implement a timeout period to prevent the client application from having to wait indefinitely for a Web Service call to complete. The proxy class that is auto-generated by Visual Studio .NET contains additional methods and properties beyond the methods used in our Web Service examples. One of these is the Timeout property.

    The Timeout property is part of the WebClientProtocol base class from which our proxy class is derived. The property indicates the time a Web Service client waits for a synchronous Web Service request to complete (in milliseconds). The default value for the property is -1, which represents infinity. This indicates that the default behavior of the Web Service client is to wait indefinitely for the remote procedure call to complete. Here's a C# sample code setting the timeout value to 15 seconds:

    	//Set timeout value to 15 seconds
    	objWebService.Timeout = 15000;

    Note that even when a Web Service client has the Timeout property set to infinity with a value of -1, the server hosting the Web Service may still cause the request to time out on the server side. In this case, an exception would be raised to the client application.

    Providing a Cancel Method :
    When using the asynchronous process model in your client application, you have to consider how your application will behave if the callback mechanism doesn't return within an acceptable timeframe. You may decide to abort the call in your code and continue with other processing. The problem with this approach is that it doesn't necessarily free up the resources on the server. Providing a Cancel method that stops the server-side processing gracefully can solve this issue.

    Sadly, the IAsyncResult interface does not provide a Cancel method. This is because it's difficult to provide a universal mechanism for stopping all processes gracefully. In many implementations there can be no guarantee that the server will be able to cancel the BeginInvoke call on the Web Service proxy class.

    Because a Cancel methodology will vary from implementation to implementation, exposing and implementing a Cancel method is up to the implementer of the proxy class that exposes BeginInvoke and EndInvoke.

    Note that the .NET framework imposes certain design guidelines on how to implement a Cancel method. Here are the things to consider:

    • By definition, a Cancel method is a request to cancel processing of the BeginInvoke method after a desired timeout period has expired.
    • The client can only make a Cancel request; the server may choose not to honor it.
    • The client should not assume that the server has stopped processing the request completely after it receives notification that the method has been canceled. In other words, the client is recommended not to destroy server resources such as file objects, as the server may still be actively using them.
    • The IsCancelled property will be set to True if the call was canceled and the IsCompleted property will be set to True after the server has completed processing of the call. It is illegal for the server to use any client-supplied resources outside of the agreed-upon sharing semantics after it sets the IsCompleted property to True. So it is safe for the client to destroy the server resources after the IsCompleted property returns True.

    Asynchronous Server Processing :
    Our discussion has centered around the asynchronous process originating from the calling application. In this scenario, only the client benefits from the asynchronous mechanism. The process running the Web Services code may still be running synchronously. The scenario that we've been discussing is illustrated here:

    Synchronous Processing
    Synchronous Processing

    In this scenario, the client gets a call object from the proxy manager and uses it to make the asynchronous call to the server. The called object turns around and makes synchronous calls on the server to the business tier. Because the Web Service thread is blocked during the synchronous call, scalability may be hampered.

    If you want the server to asynchronously process requests, you must do a little more work. There are two more modes of operation to consider when using asynchronous interfaces. The second mode is the exact opposite. The client makes a synchronous call to the proxy. The proxy then turns around and makes a call to the server. That call is processed asynchronously:

    Asynchronous Processing
    Asynchronous Processing

    In the third mode, the client makes an asynchronous call, and that call is processed asynchronously, just as the client wanted:


    Asynchronous Processing

    The Web Services themselves, running on a web server, can be designed to be synchronous or asynchronous. A synchronous design allows the ASP.NET thread of execution to run until it's complete, but if a long- running process is invoked server side, other requests will be blocked. This can stall the ASP.NET worker thread forcing other applications' requests to queue, ultimately resulting in scalability and performance issues. Designing the server-side Web Service to run asynchronously will allow ASP.NET to manage threads of execution more effectively.

    Summary :
    As we've learned, ASP.NET Web Services support both synchronous and asynchronous client invocations. There are benefits to both approaches, and the programmer has to weigh the pros and cons of each approach when deciding how to implement their client application.

    The .NET framework provides a rich set of base classes and services to make it easier to develop asynchronous communication mechanisms. One of the key features in the .NET framework that makes asynchronous programming possible are the .NET delegate classes. Delegates are type-safe self-describing function pointers.

    Invoking Web Services asynchronously from within a .NET client application follows the same design pattern used by the .NET framework for invoking asynchronous processes. The Web Service proxy class encapsulates the mechanism for calling a Web Service asynchronously. In this chapter we have discussed the following:

    • How to invoke Web Services both synchronously and asynchronously
    • How to design an asynchronous application in .NET
    • What factors should be taken into consideration when developing asynchronous Web Service calls

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      NET (110)

    New

    Hot