Microsoft & .NET.NETAsynchronous Web Services for Visual Basic .NET

Asynchronous Web Services for Visual Basic .NET

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

From Paul Kimmel’s CodeGuru.com column, Visual Basic Today

This article is about asynchronous Web Services. I have assumed that you know how to implement a basic Web Service and invoke methods in that service. If you need basic information too, then read one of my earlier articles on Web Services (or, better yet pick up a copy of Sams Visual Basic .NET Unleashed.)

Asynchronous Web Services rely on the basic asynchronous behavior built into .NET; it is part of the multithreading model of .NET. The basic idea is that you can invoke a Web Method asynchronously, which means it returns before it has finished the computation, and the Web Service will tell you at a later time when it has finished. Collectively, all of this technology relies on the CodeDOM, multithreading, SOAP, HTTP, and delegates, which illustrates why a well-architected platform is essential. Fortunately, .NET is designed so that you don’t really have to master any of these technologies to use asynchronous Web Services. The hardest thing you have to learn to do is to use delegates.

When you are finished reading this article you will have the basic information necessary to invoke Web Methods asynchronously. We’ll use a basic Web Service that returns simple data, pretending the process is long enough to warrant an asynchronous call. In July 19th’s article, I wrote about calculating prime numbers. I will use a Web Service based on the ability to calculate prime numbers, returning a Boolean to indicate prime-ness.

Integrating a Web Service into Your Application

The basic steps for integrating a Web Service are reviewed in the numbered-list that follows for convenience.

  1. Create the solution for you client application
  2. Use UDDI to find a Web Service (or you can use a known Web Service)
  3. Select Project|Add Web Reference, entering the URL for the Web Service in the Address bar. (This process is just like browsing in internet explorer)
  4. When you have navigated to the URL of the Web Service, the Add Web Reference button should be enabled in the Add Reference dialog. Click Add Reference

After you have selected the .asmx file representing the Web Service and added the reference, a new entry will be added to your project in the Web References folder. The folder name will be Web References (see figure 1) and a namespace, representing the Web Service host will be added to that namespace. There will be three files with the extensions .map, .disco, and .wsdl. Collectively, this information points at our Web Service.

There is one other file that was added to the Web References folder that doesn’t show up in the Server Explorer, Reference.vb. Reference.vb contains a proxy class that inherits from System.Web.Services.Protocols.SoapHttpClientProtocol; this class is a proxy-or wrapper-for the Web Service. The proxy class is generated using the .NET CodeDOM technology and is responsible for marshalling calls between your application and the Web Service, making Web Services easier to use.

Tip: To obtain a Web Service description you can type the URL followed by the query WSDL. For example, on my machine I can obtain a Web Service description of the Primes service by typing http://localhost/primes/service1.asmx?wsdl in the Address bar of Internet Explorer.

Importantly, the proxy class contains three methods for every Web Method. One is a proxy for the synchronous version of the Web Method, and the other two are proxy methods for the SoapHttpClientProtocol.BeginInvoke and SoapHttpClientProtocol.EndInvoke. That is, the second pair of methods represents proxies for asynchronous invocation of the Web Service.

Invoking a Web Method Asynchronously

We can figure out how to invoke the Web Service asynchronously by examining the proxy methods. Listing 1 shows the proxy methods for the Web Method IsPrime.

Listing 1: Asynchronous proxy methods for the Web Method IsPrime.

Public Function BeginIsPrime(ByVal number As Long, _
  ByVal callback As System.AsyncCallback, _
  ByVal asyncState As Object) As System.IAsyncResult

  Return Me.BeginInvoke("IsPrime", New Object() {number}, _
    callback, asyncState)

End Function
        
Public Function EndIsPrime( _
  ByVal asyncResult As System.IAsyncResult) As Boolean
  
  Dim results() As Object = Me.EndInvoke(asyncResult)
  Return CType(results(0),Boolean)

End Function

As the name suggests we call BeginIsPrime to initiate the asynchronous call. The proxy for BeginInvoke is a function that returns an interface IAsyncResult. The return value is used to synchronize interaction between the client and the Web Service. The first parameter is the value we pass to the Web Service; in this instance it is the number that we want to check for prime-ness. The second parameter is the callback. The second parameter will be the address of the method we want the Web Service to invoke when the Web Method has finished processing. The type of the callback method is the delegate AsyncCallback. The third argument is any additional object we want to pass through to the Web Service and the callback method. The third parameter can be used for any additional information, including simple data or objects.

The second method is called when the Web Service is ready or to block in the client until the Web Service is ready. For example, you can call the EndInvoke proxy in the callback method when the Web Service calls it. The callback is defined such that it accepts, and will receive, an IAsyncResult argument that you pass back to the EndInvoke proxy. The IAsyncResult object is used to synchronize the data exchange between the client and Web Service.

Listing 2 provides a slim example that combines all of the elements together. After the listing is an overview of the code as I wrote it.

Listing 2: Invoking a Web Method asynchronously.

1:  Imports System.Console
2:  Imports System.Threading
3:
4:  Public Class Form1
5:    Inherits System.Windows.Forms.Form
6:
7:  [ Windows Form Designer generated code ]
8:
9:    Private Sub Button1_Click(ByVal sender As System.Object, _
10:     ByVal e As System.EventArgs) Handles Button1.Click
11:
12:     ListBox1.Items.Clear()
13:     Start()
14:   End Sub
15:
16:   Private Service As localhost.Service1 = _
17:     New localhost.Service1()
18:   Private Sub Start()
19:
20:     Dim Numbers() As Long = _
21:       New Long() {103323, 2, 3, 56771, 7}
22:     Dim Number As Long
23:
24:     For Each Number In Numbers
25:       Dim Result As IAsyncResult = _
26:         Service.BeginIsPrime(Number, _
27:         AddressOf Responder, Number)
28:     Next
29:
30:   End Sub
31:
32:   Private Sub Responder(ByVal Result As IAsyncResult)
33:
34:     Dim IsPrime As Boolean = Service.EndIsPrime(Result)
35:
36:     If (InvokeRequired) Then
37:       Invoke(New MyDelegate(AddressOf AddToList), _
38:         New Object() {IsPrime, _
39:         CType(Result.AsyncState, Long)})
40:     End If
41:
42:   End Sub
43:
44:   Private Delegate Sub MyDelegate( _
45:     ByVal IsPrime As Boolean, ByVal Number As Long)
46:
47:   Private Sub AddToList(_
48:     ByVal IsPrime As Boolean, ByVal Number As Long)
49:
50:     Const Mask As String = _
51:       "{0} {1} a prime number"
52:
53:     Dim Filler() As String = New String() {"is not", "is"}
54:     ListBox1.Items.Add( _
55:       String.Format(Mask, Number, _
56:     Filler(Convert.ToInt16(IsPrime))))
57:
58:   End Sub
59:
60: End Class

(The code generated by the designer is condensed-simulating Code Outlining in Visual Studio .NET-on line 7.) The basic idea is that the consumer sends several inquiries about possible prime numbers. Large prime numbers take longer to calculate than prime numbers; so the client application is designed to send every number asynchronously rather than get bogged down on big prime candidates.

The process is initiated in the Button1_Click event on line 13 when Start is invoked. (The actual application sends the same numbers ever time, but you could easily make this dynamic, too.)

Start is defined on lines 18 through 30 in listing 1. An array of candidate numbers is created on lines 20 and 21, demonstrating inline initialization in Visual Basic .NET. As you can see the largest number is first. In a synchronous application the remaining numbers would wait in line until 103323 was evaluated. In our model all requests will be sent and the results displayed as they are available. The For Each loop manages sending every number to be processed, representing ongoing work while preceding requests are being serviced by the Web Service.

The code we are interested in is on lines 25 through 27. Line 25 shows you how to obtain an IAsyncResult object in case we elect to block in the Start method. (We won’t.) The first argument is the Number to be evaluated by the Web Method IsPrime; the second argument is the delegate, created implicitly with the AddressOf operator, and the third argument is the Number. I passed the Number a second time for display purposes (refer to line 55). The Web Service was created on lines 16 and 17, before the Form’s constructor was called. The name localhost represents a namespace in this context and happens to be derived from the Web Service host computer.

Retrieving the Results from the Web Method

There are several ways to block while you are waiting for a Web Service to return. You can use the IAsyncResult.IsCompleted property in a loop, call Service.EndIsPrime-the EndInvoke proxy-or request theIAsyncResult.AsyncWaitHandle. In our example, we process merrily until the callback is invoked by the Web Service. The callback method is defined on lines 32 through 42. When the callback is called we can obtain the result by calling the EndInvoke proxy method as demonstrated on line 34.

Lines 36 through 40 and 44 through 58 exist in this instance due to the kind of application—a Windows Form application. When you invoke a Web Service asynchronously the callback method will be called back on a different thread than the one the Windows Forms controls reside on. As Windows Forms is not thread-safe-which means you should interact with Windows Forms controls on the same thread as the one they live on-we can use the Control.Invoke method and push the “work” onto the same thread that the control lives on. Again we use delegates to represent the work to be done.

The method InvokeRequired can be used to determine if you need to call invoke, as demonstrated on line 36. Lines 37, 38, and 39 demonstrate the Invoke method. Define a new delegate based on the signature of the procedure you want to Invoke. Create an instance of that delegate type, initializing the delegate with the address of your work procedure. Pass an array of objects matching the parameters your work-method needs. The delegate is defined on lines 44 and 45. An instance of the delegate is created on line 37, and the array of arguments is demonstrated on lines 38 and 39. Lines 38 and 39 create an array of Object inline passing a Boolean and a Long as is expected by the AddToList method. Pay attention to the fact that the delegate signature, the procedure used to initialize the delegate, and the arguments passed to the delegate all of have identical footprints.

Summary

Asynchronous Web Services depend on a lot of advanced aspects of the .NET framework, including SOAP, XML, HTTP, CodeDOM, TCP/IP, WDSL, UDDI, multithreading, and delegates, to name a view. Fortunately, these technologies exist already and work on our behalf behind the scenes for the most part. If you want to use asynchronous Web Services, the hardest thing you need to master are delegates.

Don’t let anyone trivialize Web Services. They are powerful and complicated, but the complicated part was codified by Microsoft. The end result is that Web Services are easy to consume, with only modest additional complexity to invoke them asynchronously.

Asynchronous Web Services will add some zip to your applications. Be mindful of the existence of more than one thread and you are all set.

About the Author

Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. Look for his most recent book, Visual Basic .Net Unleashed, at a bookstore near you. Also look for his upcoming book “Advanced C# Programming” from Osborne/McGraw-Hill. Paul Kimmel is available to help design and build your .NET solutions and can be contacted at pkimmel@softconcepts.com.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories