Creating Asynchronous XML Web Service Methods

To improve performance of XML Web service methods that invoke long-running methods that block their thread, you should consider exposing them as asynchronous XML Web service methods. Implementing an asynchronous XML Web service method allows that thread to execute other code when it is returned to the thread pool. This allows one more of the limited number of threads in the thread pool to execute, enhancing overall performance and scalability of the system.

In general, XML Web service methods calling methods that perform I/O operations are good candidates for asynchronous implementation. Examples of such methods include those communicating with other XML Web services, accessing remote databases, performing network I/O, and reading and writing to large files. These methods all spend the bulk of their time executing in hardware, which leaves the thread executing the XML Web service method blocked. That thread can be freed up to execute other code if the XML Web service method is implemented asynchronously.

Regardless of whether or not an XML Web service method is implemented asynchronously, clients can communicate with it asynchronously. Asynchronous communication is exposed to .NET clients within the proxy class generated by the Web Services Description Language (WSDL.EXE) tool, even if an XML Web service method is implemented synchronously. The proxy class contains Begin and End methods for communicating with each XML Web service method asynchronously. Therefore, the decision to implement an XML Web service method asynchronously or synchronously should be based upon performance.

Note   Implementing an asynchronous XML Web service method has no impact on the HTTP connection between the client and the server hosting the XML Web service. The HTTP connection is neither closed nor pooled.

To implement an asynchronous XML Web service method

Implementing an asynchronous XML Web service method follows the .NET Framework asynchronous design pattern

  1. Split a synchronous XML Web service method into two methods; each with the same base name - one with that name starting with Begin and the other End.

  2. The parameter list for the Begin method contains all the in and by reference parameters for the method's functionality plus two appended to the end.

    • By reference parameters are listed as in parameters.
    • The second from the last parameter must be an AsyncCallback. The AsyncCallback parameter allows a client to supply a delegate, which is invoked when the method completes. When an asynchronous XML Web service method calls another asynchronous method, this parameter can be passed into the second from last parameter for that method.
    • The last parameter is an Object. The Object parameter allows a caller to supply state information to the method. When an asynchronous XML Web service method calls another asynchronous method, this parameter can be passed into the last parameter for that method.
    • The return value must be of type IAsyncResult.

    The following code example is a Begin method, which has a String parameter specific to the method's functionality.

    [WebMethod]
    public IAsyncResult BeginGetAuthorRoyalties(String Author,
                      AsyncCallback callback, object asyncState) 
    [Visual Basic]
    <WebMethod()> _
    Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
       ByVal callback As AsyncCallback, ByVal asyncState As Object) _
                        As IAsyncResult
    
  3. The parameter list for the End method consists of an IAsyncResult followed by any out and by reference parameters specific to the method's functionality.

    • The return value is the same type as the return value of a synchronous XML Web service method.
    • By reference parameters are listed as out parameters.

    The following code example is an End method, which returns a AuthorRoyalties user defined type.

    [WebMethod]
    public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResult
                                       asyncResult)
    [Visual Basic]
    <WebMethod()> _
    Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
                        IAsyncResult) As AuthorRoyalties
    

    The following code example is an asynchronous XML Web service method that asynchronously communicates with another XML Web service method.

    using System;
    using System.Web.Services;
    
    [WebService(Namespace="https://www.contoso.com/")]
    public class MyService : WebService {
      public RemoteService remoteService;
      public MyService() {
         // Create a new instance of proxy class for 
         // the XML Web service to be called.
         remoteService = new RemoteService();
      }
      // Define the Begin method.
      [WebMethod]
      public IAsyncResult BeginGetAuthorRoyalties(String Author,
                      AsyncCallback callback, object asyncState) {
         // Begin asynchronous communictation with a different XML Web
         // service.
         return remoteService.BeginReturnedStronglyTypedDS(Author,
                             callback,asyncState);
      }
      // Define the End method.
      [WebMethod]
      public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResult
                                       asyncResult) {
       // Return the asynchronous result from the other XML Web service.
       return remoteService.EndReturnedStronglyTypedDS(asyncResult);
      }
    }
    [Visual Basic]
    Imports System.Web.Services
    <WebService(Namespace:="https://www.contoso.com/")> _
    Public Class MyService
       Inherits WebService
       Public remoteService As RemoteService
    
       Public Sub New()
          MyBase.New()
          ' Create a new instance of proxy class for 
          ' the XML Web service to be called.
          remoteService = New RemoteService()
       End Sub
    
       ' Define the Begin method.
       <WebMethod()> _
       Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
       ByVal callback As AsyncCallback, ByVal asyncState As Object) _
                        As IAsyncResult
          ' Begin asynchronous communictation with a different XML Web
          ' service.
          Return remoteService.BeginReturnedStronglyTypedDS(Author, _
                                callback, asyncState)
       End Function
       ' Define the End method.
       <WebMethod()> _
       Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
                        IAsyncResult) As AuthorRoyalties
          ' Return the asynchronous result from the other XML Web service.
          Return remoteService.EndReturnedStronglyTypedDS(asyncResult)
       End Function
    End Class
    

The following code example demonstrates how to chain asynchronous calls when an XML Web service method makes more than one asynchronous call and the calls must execute sequentially. The BeginGetAuthorRoyalties method makes an asynchronous call to determine whether the author passed in is valid and sets up an intermediate callback named AuthorRoyaltiesCallback to receive the results. That intermediate callback then asynchronously calls to get the royalties for that author, if the author is valid.

using System.Web.Services;
using System.Data;
using System;
// This imports the proxy class for the XML Web services
// that the sample communicates with.
using AsyncWS.localhost;

namespace AsyncWS
{
    [WebService(Namespace="https://www.contoso.com/")]
    public class MyService : System.Web.Services.WebService
    {
        public RemoteService remoteService;
        public MyService()
        {
           remoteService = new RemoteService();
        }

        [WebMethod]
        public IAsyncResult BeginGetAuthorRoyalties(String Author,
               AsyncCallback callback, Object asyncState) 
       {
          // Saves the current state for the call that gets the author's
          // royalties.
          AsyncStateChain state = new AsyncStateChain();
          state.originalState = asyncState;
          state.Author = Author;
          state.originalCallback = callback;

          // Creates an intermediary callback.
          AsyncCallback chainedCallback = new
             AsyncCallback(AuthorRoyaltiesCallback);
          return remoteService.BeginGetAuthors(chainedCallback,state);
       }
       // Intermediate method to handle chaining the 
       // asynchronous calls.
       public void AuthorRoyaltiesCallback(IAsyncResult ar)
       {
          AsyncStateChain state = (AsyncStateChain)ar.AsyncState;
          RemoteService rs = new RemoteService();

          // Gets the result from the call to GetAuthors.
          Authors allAuthors = rs.EndGetAuthors(ar);

          Boolean found = false;
          // Verifies that the requested author is valid.
          int i = 0;
          DataRow row;
          while (i < allAuthors.authors.Rows.Count && !found)
          {
             row = allAuthors.authors.Rows[i];
             if (row["au_lname"].ToString() == state.Author) 
             {
                found = true;
             }
             i++;
          }
          if (found)
          {
             AsyncCallback cb = state.originalCallback;
             // Calls the second XML Web service, because the author is
             // valid.
             rs.BeginReturnedStronglyTypedDS(state.Author,cb,state);
          }
          else
          {
            // Cannot throw the exception in this function or the XML Web
            // service will hang. So, set the state argument to the
            // exception and let the End method of the chained XML Web
            // service check for it.  
            ArgumentException ex = new ArgumentException(
              "Author does not exist.","Author");
            AsyncCallback cb = state.originalCallback;
            // Call the second XML Web service, setting the state to an
            // exception.
            rs.BeginReturnedStronglyTypedDS(state.Author,cb,ex);
          }
       }

       [WebMethod]
       public AuthorRoyalties EndGetAuthorRoyalties(IAsyncResult
                                                    asyncResult) 
       {
        // Check whehter the first XML Web service threw an exception.
        if (asyncResult.AsyncState is ArgumentException)
          throw (ArgumentException) asyncResult.AsyncState;
        else
         return remoteService.EndReturnedStronglyTypedDS(asyncResult);
       }
    }
    // Class to wrap the callback and state for the intermediate
    // asynchronous operation.
    public class AsyncStateChain 
    {
       public AsyncCallback originalCallback;
       public Object originalState;
       public String Author;
    }
}
[Visual Basic]
Imports System.Web.Services
Imports System.Data
Imports System
' This imports the proxy class for the XML Web services
' that the sample communicates with.
Imports AsyncWS_VB.localhost


Namespace AsyncWs

<WebService(Namespace:="https://www.contoso.com/")> _
Public Class MyService
    Inherits WebService
    Public remoteService As remoteService
    Public Sub New()
        MyBase.New()
        remoteService = New localhost.RemoteService()
    End Sub
    ' Defines the Begin method.
    <WebMethod()> _
    Public Function BeginGetAuthorRoyalties(ByVal Author As String, _
    ByVal callback As AsyncCallback, ByVal asyncState As Object) _
                    As IAsyncResult
        ' Saves the current state for the call that gets the author's
        ' royalties.
        Dim state As AsyncStateChain = New AsyncStateChain()
        state.originalState = asyncState
        state.Author = Author
        state.originalCallback = callback

        ' Creates an intermediary callback.
        Dim chainedCallback As AsyncCallback = New AsyncCallback( _
           AddressOf AuthorRoyaltiesCallback)
        ' Begin asynchronous communictation with a different XML Web
        ' service.
        Return remoteService.BeginGetAuthors(chainedCallback, state)
    End Function

    ' Intermediate method to handle chaining the asynchronous calls.
    Public Sub AuthorRoyaltiesCallback(ByVal ar As IAsyncResult)
        Dim state As AsyncStateChain = CType(ar.AsyncState, _
            AsyncStateChain)
        Dim rs As RemoteService = New RemoteService()

        ' Gets the result from the call to GetAuthors.
        Dim allAuthors As Authors = rs.EndGetAuthors(ar)
        Dim found As Boolean = False

        ' Verifies that the requested author is valid.
        Dim i As Integer = 0
        Dim row As DataRow
        While (i < allAuthors.authors.Rows.Count And (Not found))
            row = allAuthors.authors.Rows(i)
            If (row("au_lname").ToString() = state.Author) Then
                found = True
            End If
            i = i + 1
        End While
        If (found) Then
            Dim cb As AsyncCallback = state.originalCallback
            ' Calls the second XML Web service, because the author is
            ' valid.
            rs.BeginReturnedStronglyTypedDS(state.Author, cb, state)
        Else
          ' Cannot throw the exception in this function or the XML Web
          ' service will hang.  So, set the state argument to the
          ' exception and let the End method of the chained XML Web
          ' service check for it.  
            Dim ex As ArgumentException = New ArgumentException( _ 
                "Author does not exist.", "Author")
            Dim cb As AsyncCallback = state.originalCallback
            ' Call the second XML Web service, setting the state to an
            ' exception.
            rs.BeginReturnedStronglyTypedDS(state.Author, cb, ex)
        End If
    End Sub

    ' Define the End method.
    <WebMethod()> _
    Public Function EndGetAuthorRoyalties(ByVal asyncResult As _
            IAsyncResult) As localhost.AuthorRoyalties
        ' Return the asynchronous result from the other XML Web service.
        Return remoteService.EndReturnedStronglyTypedDS(asyncResult)
    End Function

End Class

' Class to wrap the callback and state for the intermediate asynchronous
' operation.
Public Class AsyncStateChain
   Public originalCallback As AsyncCallback
   Public originalState As Object
   Public Author As String
End Class
End Namespace

See Also

Communicating with XML Web Services Asynchronously | Including Asynchronous Calls | Building XML Web Services Using ASP.NET