Asynchronous Web Services


One specific area in which you'll want to use asynchronous WinForms applications is when calling Web services. Calling a Web service is similar to passing a message between threads, except that Web services messages travel between machines using standard protocols such as HTTP and XML. [3] Imagine a .NET Web service that calculated digits of pi using some way cool fast pi calculation engine:

[3] For thorough coverage of the whys and wherefores of Web services, you can read Real World XML Web Services: For VB and VB .NET Developers (Addison-Wesley, 2002), by Yasser Shohoud.

 
 Class CalcPiService   Inherits System.Web.Services.WebService   <WebMethod> _   Function CalcPi(digits As Integer) As String       ' Way cool fast pi calculator running on a huge processor...       Return pi.ToString()   End Function End Class 

Now imagine a version of the CalcPi program that used the Web service instead of our slow client-side algorithm to calculate pi on giant machines with huge processors (or even better, databases with more digits of pi cached than anyone could ever want or need). Although the underlying protocol of Web services is HTTP- and XML-based and we could form a Web service request fairly readily to ask for the digits of pi we're after, it's simpler to let VS.NET generate a class to make the Web services calls for you. You can do this in the Project menu using the Add Web Reference item. The Add Web Reference dialog allows you to enter the URL of the WSDL (Web Service Description Language) that describes the Web service you'd like to call. For example, after installing the Web service sample that accompanies this book, [4] you can access the WSDL via the following URL:

[4] See the ReadMe.htm file that comes with the book's samples for detailed instructions on setting them up.

http://localhost/CalcPiWebService/CalcPiService.asmx?WSDL

Accepting the WSDL in the Add Web Reference dialog will generate a client-side Web services proxy class , a helper class that turns your method calls into Web services messages. The generated proxy code for the CalcPi Web service looks like this:

 
 Namespace AsyncCalcPi.localhost   <WebServiceBindingAttribute(Name="CalcPiServiceSoap", ...)> _   Class CalcPiService       Inherits SoapHttpClientProtocol       Public Sub New()           Me.Url = _               http://localhost/CalcPiWebService/CalcPiService.asmx       End Sub       <SoapDocumentMethodAttribute(http://tempuri.org/CalcPi, ...)> _       Function CalcPi(digits As Integer) As String           ...       End Function       Function BeginCalcPi(digits As Integer, _        callback As System.AsyncCallback, asyncState As Object) _            As IAsyncResult           ...       End Function       Function EndCalcPi(asyncResult As IAsyncResult) As String           ...       End Function End Namespace 

Calling the Web service is now a matter of creating an instance of the proxy and calling the method you're interested in:

 
 Dim service As localhost.CalcPiServices = New localhost.CalcPiServices() Sub calcButton_Click(sender As Object, e As System.EventArgs)   piTextBox.Text = service.CalcPi(CInt(digitsUpDown.Value)) End Sub 

Because Web services make calls across machine and often network boundaries, you should assume they'll take a long time, and, if called synchronously, they'll block the UI thread. You can use the standard techniques discussed in this chapter to call Web service methods asynchronously, but as you can tell in the generated proxy code, there's built-in support for asynchronous operations via the BeginXxx/EndXxx method pairs, one for each method on the Web service.

The first step in retrofitting the sample application to use the Web service is to call the Web service proxy's BeginCalcPi method:

 
 Enum CalcState   Pending   Calculating   Canceled End Enum Dim state As CalcState = CalcState.Pending Dim service As localhost.CalcPiService = New localhost.CalcPiService Sub calcButton_Click(sender As Object, e As EventArgs)   Select Case state       ' Start a new calculation       Case CalcState.Pending           ' Allow canceling           state = CalcState.Calculating           calcButton.Text = "Cancel"           ' Start Web service request           service.BeginCalcPi( _               CInt(digitsUpDown.Value), _               New AsyncCallBack(PiCalculated), _               Nothing)       ' Cancel a running calculation       Case CalcState.Calculating          state = CalcState.Canceled          calcButton.Enabled = False          service.Abort() ' Fail all outstanding requests      ' Shouldn't be able to press Calc button while it's canceling      Case CalcState.Canceled          Debug.Assert(False)   End Select End Sub Sub PiCalculated(res As IAsyncResult)   ... End Sub 

The BeginCalcPi method takes the method parameters and an instance of an AsyncCallback delegate. The application provides the PiCalculated event handler to match the AsyncCallback signature. The PiCalculated method, which will be called when the Web service returns, is responsible for harvesting results.

Also, even though there is no way to get progress from a Web service (all the more reason to call it asynchronously), calls to a Web service can be canceled by a call to the Abort method. Be careful with this one, however, because it will cancel all outstanding requests, not just a specific one.

When the AsyncCallback event handler is called, it means that the Web service has returned something:

 
 Sub PiCalculated(res As IAsyncResult)   Try       ShowPi(service.EndCalcPi(res))   Catch ex As WebException       ShowPi(ex.Message)   End Try End Sub 

The call to EndCalcPi, passing in the IAsyncResult parameter, provides any results of calling the Web service. If the Web service call was successful, the return value is pi. If, on the other hand, the Web service call was unsuccessful , because it timed out or was canceled, EndCalcPi will throw a WebException. That's why PiCalculated wraps the call to EndCalcPi in a try-catch block. ShowPi will be called on to show either the digits of pi that were calculated by the Web service or the exception message (the result of a canceled Web service call is shown in Figure 14.7).

Figure 14.7. The Result of a Canceled Call to the Pi Web Service

The ShowPi method displays the results of the Web service invocation:

 
 Delegate Sub ShowPiDelegate(pi As String) Sub ShowPi(pi As String)   If Me.InvokeRequired = False Then       piTextBox.Text = pi       state = CalcState.Pending       calcButton.Text = "Calc"       calcButton.Enabled = True   Else       Dim showPI As ShowPiDelegate = _           New ShowPiDelegate(AddressOf ShowPi)       Me.BeginInvoke(showPi, New Object() {pi})   End If End Sub 

Notice that the ShowPi method contains the check to see whether an invoke is required, and it calls Control.BeginInvoke to transfer from the current thread to the UI thread, just as we've been doing for most of this chapter. This is necessary because the Web service completion notification will come in on a thread other than the UI thread.



Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net