By now you have heard how great Web Services are: easy to code, cross platform, use open standards, solve interoperability issues with the firewall, and everybody is supporting it including Microsoft, Sun, Apple, and IBM. Creating Web Services is so easy that you can find web services done as college homework assignments on Apache servers running PHP when you browse the UDDI directories of Web Services. (My favorite directory is www.xmethods.net, shown to me by Tom Howe at the last DevCon.) Consuming Web Services has been made easy by Visual Studio.NET, so easy that you can code against a web service as if it were a local object. Visual Studio.NET also gives you the option to call a Web Service asynchronously, allowing your program to do other things while it goes and fetches Web Service data. In this article we are going to take a look at how to consume Web Services asynchronously.
I am going to write a real simple Web Service called “DailyRant” in honor of the retiring Dennis Miller
A Quick Review
Before we get started, we need to understand how Visual Studio.NET helps you out in Web Services consumption, so we are going to create and consume a real simple Web Service normally (synchronously) first. To create a web service, either add a new project or add a “new item” to an existing ASP.NET project called a “Web Service” as shown in Figure 1.
Fig. 1: Adding a Web Service to a Visual Studio.NET project. Visual Studio comes preconfigured to handle Web Services as project and page types.
Visual Studio.NET adds an “asmx” file to your project for you to start coding your Web Service. An asmx file is a special type of ASP.NET page that Visual Studio.NET adds all the Web Services plumbing to for you. All you have to do is write your code. I am going to write a real simple Web Service called “DailyRant” in honor of the retiring Dennis Miller. My Web Service has one method, GetDailyRant that returns a string value (of my rant - not as funny as Dennis). My example hard codes in a rant (today’s is about the pending baseball strike), a more robust web service would use ADO.NET to get a rant from a database. The code for the Web Service is shown in Listing 1.
Imports System.Web.Services
<WebService(Namespace:="http://www.stephenforte.NET",_
Description:="A simple example Web Service")>_
Public Class DailyRant
Inherits System.Web.Services.WebService
#Region " Web Services Designer Generated Code "
<WebMethod(CacheDuration:=600,_
Description:="A daily rant by Stephen Forte")>_
Public Function GetCurrentRant() As String
Return "No baseball strike!"
End Function
End Class
Listing 1: The DailyRant Web Service. A simple web service is created in Visual Studio.NET.
Notice in that listing 1 Visual Studio imports the Web.Services namespace for you and has a whole #Region area where VS.NET generates some plumbing code for you. My code is in the <WebService> tag where I define the name of my web service, an optional description and the service’s namespace. (It is very important to change your namespace from the default namespace to distinguish your code from other peoples.) Next I write a method called GetCurrentRant in plain old Visual Basic.NET. Nothing is special about this method except the <WebMethod> tag I added that has an optional Cache Duration parameter (here I cache my service for 10 minutes) and an optional Description.
Testing Your Web Service
To test your web service all you have to do is save your work and build the solution (F5). Then you can open the asmx page via a browser and play with your Web Service as shown in Figure 2. Notice where the optional descriptions come into play (after the Web Service and Method names). You can click on the method to run it right in the browser, the asmx page creates a simple test bed for you. The results are show here:
version="1.0" encoding="utf-8" ?>
xmlns="http://www.stephenforte.net">
No baseball strike!
We are ready to start consuming the Web Service in Visual Studio.NET both synchronously and asynchronously.
Fig. 2: Testing a Web Service in a browser. Notice where the optional descriptions come into play.
Consuming a Web Service via Visual Studio.NET
Consuming this Web Service in your application is just as easy as it was to create it. To consume a Web Service in your Windows Forms or ASP.NET application, you have set a “web reference” to it. This is where Visual Studio.NET does a lot of work for you (work that we will dive into soon). To set a “web reference,” in your project, you have to point to an industry standard WSDL (Web Services Description Language) XML file. All Web Services must create a WSDL file for others to consume. To consume the Rant WSDL, open a new project (either ASP.NET or Windows Forms) and select Project|Add Web Reference from the main menu. This will present the Add Web Reference dialog.
To set a reference to the Web Service that we created, type in this URL in the address box: http://www.stephenforte.net/DailyRant.asmx?wsdl. The WSDL from my Web Service will be presented to you, so just click on the “Add Reference” button to start using the Web Service in your application.
Wait a minute, “How did you create the WSDL” you may be asking? The neat thing about using Visual Studio.NET to create a Web Service is that it will automatically create the WSDL for you and you can get there by typing in the URL to the asmx file with the ?WSDL querystring in the browser.
Using Visual Studio.NET coding Web Services asynchronously is quite easy!
Using the Proxy
By setting the reference to the web service, Visual Studio actually creates a Proxy object for you. A proxy is an intermediary that acts as a stand-in for the code you want to call. It creates a local object with all of the same method signatures as the Web Service. Requests to the Web Service are handled by the Proxy, you think that you are coding against a client side object, along with IntelliSense! Remember Richard Campbell’s articles in the summer of 2000 and our joint session at DevCon about using SOAP to call Web Services (they may not have been called WebServices at the time). We wrote about all the manual code you had to write to get this to work on the client. Now the proxy has eliminated all of that work! (and killed our very popular joint session at DevCon!)
The proxy is responsible for marshalling, or managing, your call to the web method over the machine boundaries. Internally, the proxy uses SOAP and HTTP/TCPIP to work its magic. If the web service changes, all you have to do is right click on the reference (proxy) in your project explorer and select “Update Web Reference” from the pop-up menu.
Using Web Services in non.NET Environments:
You can still take the easy way out and use a proxy object in a non-.NET environment. Visual Basic 6.0 has a SOAP Toolkit (SOAP and ROPE objects) and non-Microsoft platforms have a Web Services proxy creation toolkit as well.
Consuming the Web Service Synchronously
The default way of calling a Web Service method (or any method for that manner) is synchronously. What happens here is you call a method and then your program waits for a return value. This is usually the best way to call a web service that your application needs data from to do the next step of processing. To call the GetCurrentRant web service, create a button on your form and on the click event, create an object that points to the Web Service’s proxy and execute the method. The code to do that is shown here:
Dim oWS As New net.stephenforte.www.DailyRant()
MsgBox(oWS.GetCurrentRant, MsgBoxStyle.Information)
The results will come back rather quick (unless all of you are doing this at the same time and my site is nice and slow!). That is all there is too it, you have just consumed a Web Service in Visual Studio.NET!
Consuming the Web Service Asynchronously
In situations where the call to the Web Service is both slow and non-process oriented, asynchronous processing can help. This architecture will call the method, allow the application to go about its business and then notify the client when the result is ready via a callback method as shown in figure 4. First you call the method in asynchronous mode and the proxy will call the Web Service for you. When the method is done, it will issue a callback to the client application, which triggers an event for you to handle and get the data from a delegate. That is all there is to it, so we will continue with a simple example. Before we begin, however, a simple warning: asynchronous processing uses a thread for each call, so you have to be careful not to overdo it.
Fig. 3: Asynchronous Web Service architecture. The Visual Studio.NET Asynchronous callback architecture.
That being said, a perfect example is the customer record screen at Fidelity Investments (I know this screen well since I worked on it 8 years ago and surprisingly it has not changed all that radically in the last decade.) When a broker or customer service agent pulls up a customer record, they are presented with all of the data and calculations from Fidelity’s proprietary systems, all databases Fidelity can control the speed and performance of. Let’s say that Fidelity wanted to pull up something from outside the Fidelity database like a criminal records search on the person’s social security number or even something as simple as the local weather from the caller’s zip code. This data can come on the screen later on, after the Fidelity data has loaded. The screen can use an asynchronous callback to accomplish this.
Implementing Started with Asynchronous Callbacks
For this example I am going to choose a more fun example than my daily rant. There is a free web service out there that accepts an Ebay auction ID and returns the current price of that auction. Let’s create a simple application with two textboxes to hold the Auction IDs and we will call the Web Service twice asynchronously to get the results shown in figure 4 (and available for download from advisor.com.)
Fig. 4: Calling Web Services Asynchronously. A sample eBay Auction ID application.
The first thing that you have to do is import the threading namespace:
This namespace allows you to use AsyncCallback objects you need to act as a delegate for your callback. To create these objects add this code in the modules reference section:
Private acAuction1 As AsyncCallback
Private acAuction2 As AsyncCallback
The objects you just created are going to be used to get the data back from the asynchronous callback. You need to create an event handler for each of them. You do this by placing this code in the form load event:
Private Sub Form1_Load(_
ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles MyBase.Load
acAuction1 = New AsyncCallback(_
AddressOf Me.onCompletedAuction1)
acAuction2 = New AsyncCallback(_
AddressOf Me.onCompletedAuction2)
End Sub
You have to create the two event handlers manually: onCompletedAuction1 and onCompletedAuction2. This is where you will handle the callback, for now just create the stub here:
Private Sub onCompletedAuction1(_
ByVal asyncResult As IAsyncResult)
Private Sub onCompletedAuction2(_
ByVal asyncResult As IAsyncResult)
When you call a web method via the proxy created by Visual Studio.NET, it will automatically create a new asynchronous method for each of the normal Web Methods for you. For example, our eBay example (set a web reference to: http://www.xmethods.net/sd/2001/EBayWatcherService.wsdl) has a method called getCurrentPrice. You can call this normally (synchronously) via the proxy by calling getCurrentPrice. If you want to call it asynchronously, the proxy adds two methods Begin..methodname and End..methodname for you to call asynchronously. For getCurrentPrice, the proxy adds BegingetCurrentPrice and EndgetCurrentPrice. Let’s start by calling BegingetCurrentPrice like so:
Dim oWs As New net.xmethods.www.eBayWatcherService()
oWs.BegingetCurrentPrice(_
Me.txtAuction1.Text, acAuction1, 0)
oWs.BegingetCurrentPrice(_
Me.txtAuction2.Text, acAuction2, 0)
In this example we are calling the method twice, once for each textbox’s auction ID. We pass in the Auction ID, the delegate we created above and an optional status object that I left blank with a zero (I choose not to cover that here). Now all you have to do is wait until the method calls are done and handle them. Sometimes when you run this, the second call finishes first and other times it does not. The code to handle the callbacks is shown in listing 2.
Private Sub onCompletedAuction1(ByVal asyncResult As IAsyncResult)
Dim oWS As New net.xmethods.www.eBayWatcherService()
Dim str As String = oWS.EndgetCurrentPrice(asyncResult)
Me.lblAuction1.Text = Format(str, "Currency")
End Sub
Private Sub onCompletedAuction2(ByVal asyncResult As IAsyncResult)
Dim oWS As New net.xmethods.www.eBayWatcherService()
Dim str As String = oWS.EndgetCurrentPrice(asyncResult)
Me.lblAuction2.Text = Format(str, "Currency")
End Sub
Listing 2: Handling the callbacks. Using the End..methodname to handle the callback.
Listing 2 show the methods that will handle the callback. These run on a different thread and fire when the asynchronous method has completed. To handle the callback, just create a new object pointing to the proxy and set the return value equal to the EndgetCurrentPrice method with the delegate passed in as a parameter. That is it! Then you can do whatever you want with the return value, in this case we assign it to a label next to the textbox.
Conclusion
When you have a need to display data from a Web Service on a screen that is not dependent on other items, you can explore using asynchronous callbacks to speed up the task and free your application to process other items. Using Visual Studio.NET and the proxy it creates for you when you set a reference to a Web Service, coding Web Services asynchronously is quite easy! Give it a try and let me know how it works.