Previous section   Next section

16.3 Creating the Proxy

Before you can create a client application to interact with the calculator web service, you must first create a proxy class. Once again, you can do this by hand, but that would be hard work. The folks at Microsoft have provided a tool called wsdl that generates the source code for the proxy based on the information in the WSDL file.

To create the proxy, enter wsdl at the Visual Studio .NET command-line prompt followed by the path to the WSDL contract and the /l:vb flag:

wsdl <url> /l:vb

For example, you might enter:

wsdl http://localhost/VBWSCalc/Service1.asmx?wsdl /l:vb

The flag /l:vb tells wsdl to produce a Visual Basic .NET file.

The result is the creation of a Visual Basic .NET client file named Service1.vb, an excerpt of which appears in Example 16-3. You must add the namespace WSCalc because you'll need it when you build your client (the tool does not insert it for you).

Example 16-3. Sample client code to access the calculator web service
'-------------------------------------------------------------------------
' <autogenerated>
'     This code was generated by a tool.
'     Runtime Version: 1.0.3705.288
'
'     Changes to this file may cause incorrect behavior and will be lost if 
'     the code is regenerated.
' </autogenerated>
'------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports System
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Xml.Serialization

'
'This source code was auto-generated by wsdl, Version=1.0.3705.288.
'

'<remarks/>
Namespace VBWSCalc

    <System.Diagnostics.DebuggerStepThroughAttribute( ), _
    System.ComponentModel.DesignerCategoryAttribute("code"), _
    System.Web.Services.WebServiceBindingAttribute(Name:="Service1Soap", [Namespace]:
="http://tempuri.org/")> _
    Public Class Service1
        Inherits System.Web.Services.Protocols.SoapHttpClientProtocol

        '<remarks/>
        Public Sub New( )
            MyBase.New( )
            Me.Url = "http://localhost/ProgrammingVBNET/VBWSCalc/Service1.asmx"
        End Sub

        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/
Add", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", 
Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.
Services.Protocols.SoapParameterStyle.Wrapped)> _
        Public Function Add(ByVal x As Double, ByVal y As Double) As Double
            Dim results( ) As Object = Me.Invoke("Add", New Object( ) {x, y})
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        Public Function BeginAdd(ByVal x As Double, ByVal y As Double, ByVal callback As 
System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
            Return Me.BeginInvoke("Add", New Object( ) {x, y}, callback, asyncState)
        End Function

        '<remarks/>
        Public Function EndAdd(ByVal asyncResult As System.IAsyncResult) As Double
            Dim results( ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/
Subtract", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.
org/", Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:
=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)> _
        Public Function Subtract(ByVal x As Double, ByVal y As Double) As Double
            Dim results( ) As Object = Me.Invoke("Subtract", New Object( ) {x, y})
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        Public Function BeginSubtract(ByVal x As Double, ByVal y As Double, ByVal callback 
As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
            Return Me.BeginInvoke("Subtract", New Object( ) {x, y}, callback, asyncState)
        End Function

        '<remarks/>
        Public Function EndSubtract(ByVal asyncResult As System.IAsyncResult) As Double
            Dim results( ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/
Mult", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", 
Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.
Services.Protocols.SoapParameterStyle.Wrapped)> _
        Public Function Mult(ByVal x As Double, ByVal y As Double) As Double
            Dim results( ) As Object = Me.Invoke("Mult", New Object( ) {x, y})
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        Public Function BeginMult(ByVal x As Double, ByVal y As Double, ByVal callback As 
System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
            Return Me.BeginInvoke("Mult", New Object( ) {x, y}, callback, asyncState)
        End Function

        '<remarks/>
        Public Function EndMult(ByVal asyncResult As System.IAsyncResult) As Double
            Dim results( ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/
Div", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", 
Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.
Services.Protocols.SoapParameterStyle.Wrapped)> _
        Public Function Div(ByVal x As Double, ByVal y As Double) As Double
            Dim results( ) As Object = Me.Invoke("Div", New Object( ) {x, y})
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        Public Function BeginDiv(ByVal x As Double, ByVal y As Double, ByVal callback As 
System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
            Return Me.BeginInvoke("Div", New Object( ) {x, y}, callback, asyncState)
        End Function

        '<remarks/>
        Public Function EndDiv(ByVal asyncResult As System.IAsyncResult) As Double
            Dim results( ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/
Pow", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", 
Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.
Services.Protocols.SoapParameterStyle.Wrapped)> _
        Public Function Pow(ByVal x As Double, ByVal y As Double) As Double
            Dim results( ) As Object = Me.Invoke("Pow", New Object( ) {x, y})
            Return CType(results(0), Double)
        End Function

        '<remarks/>
        Public Function BeginPow(ByVal x As Double, ByVal y As Double, ByVal callback As 
System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
            Return Me.BeginInvoke("Pow", New Object( ) {x, y}, callback, asyncState)
        End Function

        '<remarks/>
        Public Function EndPow(ByVal asyncResult As System.IAsyncResult) As Double
            Dim results( ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0), Double)
        End Function
    End Class
End Namespace

This complex code is produced by the wsdl tool to build the proxy DLL you will need when you build your client. The file uses attributes extensively (see Chapter 8), but with your working knowledge of Visual Basic .NET you can extrapolate at least how some of it works.

The file starts by declaring the Service1 class that derives from the class SoapHttpClientProtocol, which occurs in the namespace called System.Web.Services.Protocols:

Public Class Service1
    Inherits System.Web.Services.Protocols.SoapHttpClientProtocol

The constructor sets the URL property inherited from SoapHttpClientProtocol to the URL of the .asmx page you created earlier.

The Add( ) method is declared with a host of attributes that provide the SOAP plumbing to make the remote invocation work.

The WSDL application has also provided asynchronous support for your methods. For example, for the Add( ) method, it also created BeginAdd( ) and EndAdd( ). This allows you to interact with a web service without performance penalties.

To build the proxy, place the code generated by WSDL into a Visual Basic .NET Library project in Visual Studio .NET and then build the project to generate a DLL. You may need to add a reference to System.Web.Services. In any case, be sure to write down the location of that DLL, as you will need it when you build the client application.

To test the web service, create a very simple Visual Basic .NET console application. The only trick is that in your client code you need to add a reference to the proxy DLL just created. Once that is done, you can instantiate the web service, just like any locally available object:

Dim theWebSvc As _
    New ClassLibrary1.VBWSCalc.Service1( )

You can then invoke the Pow( ) method as if it were a method on a locally available object:

Dim i As Integer
Dim j As Integer
For i = 2 To 10
    For j = 1 To 10
        Console.WriteLine("{0} to the power of {1} = {2}", _
            i, j, theWebSvc.Pow(i, j))
    Next
Next

This simple loop creates a table of the powers of the numbers 2 through 9, displaying for each the powers 1 through 9. The complete source code and an excerpt of the output is shown in Example 16-4.

Example 16-4. A client program to test the calculator web service
Class Tester
    Public Sub Run( )
        Dim theWebSvc As _
            New ClassLibrary1.VBWSCalc.Service1( )
        Dim i As Integer
        Dim j As Integer
        For i = 2 To 10
            For j = 1 To 10
                Console.WriteLine("{0} to the power of {1} = {2}", _
                    i, j, theWebSvc.Pow(i, j))
            Next
        Next
    End Sub

    Public Shared Sub Main( )
        Dim t As New Tester( )
        t.Run( )
    End Sub

End Class

Output (excerpt):
2 to the power of 1 = 2
2 to the power of 2 = 4
2 to the power of 3 = 8
2 to the power of 4 = 16
2 to the power of 5 = 32
2 to the power of 6 = 64
2 to the power of 7 = 128
2 to the power of 8 = 256
2 to the power of 9 = 512
2 to the power of 10 = 1024
3 to the power of 1 = 3
3 to the power of 2 = 9
3 to the power of 3 = 27
3 to the power of 4 = 81
3 to the power of 5 = 243
3 to the power of 6 = 729
3 to the power of 7 = 2187
3 to the power of 8 = 6561
3 to the power of 9 = 19683
3 to the power of 10 = 59049

Your calculator service is now more available than you might have imagined (depending on your security settings) through the web protocols of HTTP-Get, HTTP-Post, or SOAP. Your client uses the SOAP protocol, but you could certainly create a client that would use HTTP-Get:

http://localhost/VBWSCalc/Service1.asmx/Add?x=23&y=22

In fact, if you put that URL into your browser, the browser will respond with the following answer:

<?xml version="1.0" encoding="utf-8"?>
<double xmlns="http://www.libertyAssociates.com/webServices/">45</double>

The key advantage SOAP has over HTTP-Get and HTTP-Post is that SOAP can support a rich set of datatypes, including all of the Visual Basic .NET intrinsic types (Integer, Double, etc.), as well as enumerations, classes, structures, and ADO.NET DataSets, and arrays of any of these types.

Also, while HTTP-Get and HTTP-Post protocols are restricted to name/value pairs of primitive types and enums, SOAP's rich XML grammar offers a more robust alternative for data exchange.


  Previous section   Next section
Top