JavaScript Editor Javascript validator     Javascripts

Main Page


Previous Section Next Section

17.2 Creating the Proxy

As described in Chapter 15 and shown schematically in Figure 15-1, a web service is consumed by a client application by use of a proxy. A proxy is a substitute, or local stand-in, for the web service. Once the proxy is created and registered with the consuming application, then method calls can be made against the web service. In actuality, those method calls will be made against the local proxy. It will seem to the consuming application that the web service is local to the application.

There are two ways to generate the proxy. The first way (described in the next section) is to generate the source code for the proxy class manually and compile that into the proxy DLL. The advantages to this method are:

  • You do not need to use Visual Studio .NET.

  • The command-line approach offers more flexibility and features over Visual Studio .NET.

The alternative method is to allow Visual Studio .NET to create the proxy and register it with the consuming application in a single step. The advantage to this method is that it is much less work. Using Visual Studio .NET will be demonstrated shortly.

17.2.1 Manually Generating the Proxy Class Source Code

To create the proxy, use another command-line utility called wsdl.exe. This utility takes a WSDL file as input. The WSDL file can either be stored locally, having been previously created using the disco command-line utility, or it can be generated on the fly from the web service file itself. The following two command lines will yield the same result, assuming that the local WSDL file came from the remote .asmx file:

wsdl csStockTicker.WSDL
wsdl http://localhost/ProgAspNet/csStockTicker.asmx?wsdl

Alternatively, the WSDL utility can take a .discomap file (described earlier in Section 17-1) created by the disco utility as input.

The output from the WSDL utility is a source code file containing the proxy class, which can then be compiled into a library, or dll, file. The default language for this output source is C#. To change the language of the output file, use the /language: parameter, or /l: for short. Valid values for the language parameter are CS, VB, or JS, for C#, VB.NET, and JScript.NET, respectively. So, to force the output to be VB.NET, you would use a command line similar to:

wsdl /l:VB http://localhost/ProgAspNet/vbStockTicker.asmx?wsdl

By default, the first component of the output filename is based on the input file as follows. If the WebService attribute in the .asmx file has a Name property, then the output file will have that name. If not, the output name will have the name of the web service class. Note that every output filename also has an extension corresponding to the language.

For example, suppose that the file vbStockTicker.asmx has the following WebService attribute and class definition:

<WebService (Description:="A stock ticker using VB.NET.", _
         Name:="StockTicker", _
         Namespace:="www.LibertyAssociates.com")> _
public class vbStockTicker
    inherits System.Web.Services.WebService

If the WSDL utility is run against the WSDL file generated from this .asmx file with the language set to VB, then the output filename would be StockTicker.vb. However, if the Name property is removed from the .asmx source file, then the output name will be vbStockTicker.vb. By default the output file will be in the current directory of the command prompt.

You can specify both the output filename and location by using the /out: parameter, or /o: for short. For example, the following command line will force the output file to have the name Test.vb and be located in the bin directory below the current directory:

wsdl /l:VB /o:bin\test.vb 
     http://localhost/ProgAspNet/vbStockTicker.asmx?WSDL

Table 17-2 shows some of the other switches available to the WSDL utility.

Table 17-2. WSDL utility switches

Parameter

Description

/nologo

Suppress the Microsoft banner.

/namespace:<namespace>

Specify the namespace for the generated proxy. The default is the global namespace.

/protocol:<protocol>

Specify the protocol to implement. Valid values are HttpGet, HttpPost, or SOAP. The default is SOAP.

/username:<username>/password:<password>/domain:<domain>

Credentials to use when connecting to a server that requires authentication.

For a complete list of parameters for wsdl.exe, enter the following from the command line:

wsdl /?

17.2.2 Proxy Class Details

Compare the beginning of the original web service source file, csStockTicker.asmx, reproduced in Example 17-1, with the beginning of the generated source code for the proxy class, StockTicker.cs, shown in Example 17-2.

Example 17-1. Beginning of csStockTicker.asmx
<%@ WebService Language="C#" Class="ProgAspNet.csStockTicker" %>

using System;
using System.Web.Services;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

namespace ProgAspNet
{
   [WebService (Description="A stock ticker using C#.",
            Name="StockTicker",
            Namespace="www.LibertyAssociates.com")] 
   public class csStockTicker : System.Web.Services.WebService
        {
        //  Construct and fill an array of stock symbols and prices.
        //  Note: the stock prices are as of 7/4/01.
      string[,] stocks = 
      {
         {"MSFT","Microsoft","70.47"},
         {"DELL","Dell Computers","26.91"},
         {"HWP","Hewlett Packard","28.40"},
         {"YHOO","Yahoo!","19.81"},
         {"GE","General Electric","49.51"},
         {"IBM","International Business Machine","112.98"},
         {"GM","General Motors","64.72"},
         {"F","Ford Motor Company","25.05"}
      };

      public class Stock
      {
         public string StockSymbol;
         public string StockName;
         public double Price;
         public StockHistory[] History = 
               new StockHistory[2];
      }

      public class StockHistory
      {
         public DateTime TradeDate;
         public double Price;
      }

      [WebMethod(Description="Returns stock history for " +
                  "the stock symbol specified.")]
      public Stock GetHistory(string StockSymbol)
      {
         Stock stock = new Stock(  );

         //  Iterate through the array, looking for the symbol.
         for (int i = 0; i < stocks.GetLength(0); i++)
         {
            //  Do a case-insensitive string compare.
            if (String.Compare(StockSymbol, stocks[i,0], true) == 0)
            {
               stock.StockSymbol = StockSymbol;
               stock.StockName = stocks[i,1];
               stock.Price = Convert.ToDouble(stocks[i,2]);

               //  Populate the StockHistory data.
               stock.History[0] = new StockHistory(  );
               stock.History[0].TradeDate = 
                   Convert.ToDateTime("5/1/2001");
               stock.History[0].Price = Convert.ToDouble(23.25);

               stock.History[1] = new StockHistory(  );
               stock.History[1].TradeDate = 
               Convert.ToDateTime("6/1/2001");
               stock.History[1].Price = Convert.ToDouble(28.75);

               return stock;
            }
         }
         stock.StockSymbol = StockSymbol;
         stock.StockName = "Stock not found.";
         return stock;
      }
Example 17-2. Beginning of Proxy class source code file StockTicker.cs
//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 1.1.4322.573
//
//     Changes to this file may cause incorrect behavior and will be lost 
//     if the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------

// 
// This source code was auto-generated by wsdl, Version=1.0.2914.16.
// 
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.Web.Services;


[System.Web.Services.WebServiceBindingAttribute(Name="StockTickerSoap", Namespace="www.
LibertyAssociates.com")]
public class StockTicker : System.Web.Services.Protocols.SoapHttpClientProtocol {
    
    [System.Diagnostics.DebuggerStepThroughAttribute(  )]
    public StockTicker(  ) {
        this.Url = "http://localhost/ProgAspNet/csStockTicker.asmx";
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute(  )]
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute(
        "www.LibertyAssociates.com/GetHistorys",
         RequestNamespace="www.LibertyAssociates.com", 
         ResponseNamespace="www.LibertyAssociates.com", 
         Use=System.Web.Services.Description.SoapBindingUse.Literal, 
         ParameterStyle=System.Web.Services.Protocols.
              SoapParameterStyle.Wrapped)]
    public Stock GetHistory(string StockSymbol) {
        object[] results = this.Invoke("GetHistory", new object[] {
                    StockSymbol});
        return ((Stock)(results[0]));
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute(  )]
    public System.IAsyncResult BeginGetHistory(string StockSymbol, 
               System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("GetHistory", new object[] {
                    StockSymbol}, callback, asyncState);
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute(  )]
    public Stock EndGetHistory(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((Stock)(results[0]));
    }
    ...
}

Here's part of the VB .NET proxy class:

'------------------------------------------------------------------------------
' <autogenerated>
'     This code was generated by a tool.
'     Runtime Version: 1.1.4322.573
'
'     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 Microsoft.VSDesigner,
' Version 1.1.4322.573.
'
Namespace localhost
    
    '<remarks/>
    <System.Diagnostics.DebuggerStepThroughAttribute(  ), _
     System.ComponentModel.DesignerCategoryAttribute("code"), _
     System.Web.Services.WebServiceBindingAttribute( _
     Name:="vbStockTickerSoap", _
     [Namespace]:=" www.LibertyAssociates.com"),  _
     System.Xml.Serialization.XmlIncludeAttribute( _
     GetType(System.Object(  )))>  _
    Public Class vbStockTicker
        Inherits System.Web.Services.Protocols.SoapHttpClientProtocol
        
        '<remarks/>
        Public Sub New(  )
            MyBase.New
            Me.Url = "http://localhost/WebApplication1/vbStockTicker.asmx"
        End Sub
        
        '<remarks/>
        <System.Web.Services.Protocols.SoapDocumentMethodAttribute( _
         "www.LibertyAssociates.com/GetHistory", _
         RequestNamespace:=" www.LibertyAssociates.com", _
         ResponseNamespace:=" www.LibertyAssociates.com", _
         Use:=System.Web.Services.Description.SoapBindingUse.Literal, _
        ParameterStyle:=SoapParameterStyle.Wrapped)>  _
        Public Function GetHistory(ByVal StockSymbol As String) As Stock
            Dim results(  ) As Object = Me.Invoke("GetHistory", _
              New Object(  ) {StockSymbol})
            Return CType(results(0),Stock)
        End Function
        
        '<remarks/>
        Public Function BeginGetHistory(ByVal StockSymbol As String, _
          ByVal callback As System.AsyncCallback, _
          ByVal asyncState As Object) As System.IAsyncResult 
            Return Me.BeginInvoke("GetHistory", _
              New Object(  ) {StockSymbol}, callback, asyncState)
        End Function
        
        '<remarks/>
        Public Function EndGetHistory(ByVal asyncResult As _
          System.IAsyncResult) As Stock
            Dim results(  ) As Object = Me.EndInvoke(asyncResult)
            Return CType(results(0),Stock)
        End Function
        
        '<remarks/>
        ...
    End Class
End Namespace

There is no need to understand fully all the nuances of the proxy class source code file. But there are several points worth noting:

  • The namespaces referenced with the using statements at the beginning of Example 17-1 and Example 17-2 are not the same. This is because the proxy class is not actually using System.Data. It is merely taking the call to the method that will ultimately use System.Data, wrapping it in the proper protocol (SOAP in this case), and passing it over the Internet to the web service. Therefore, the only namespaces actually needed by the proxy class are those necessary for interacting with a web service, serializing the data into an XML data stream, and sending and receiving those XML packages.

  • The StockTicker class inherits from SoapHttpClientProtocol rather than from WebService. This inherited class provides the methods for the proxy to talk to the web service using the SOAP protocol.

  • Immediately following the StockTicker class declaration in the generated proxy is a constructor, which is a public method with the same name as the class. In the constructor, the URL of the web service is specified.

    A constructor is the method in a class that is invoked when the class is first instantiated. The constructor is used to initialize the class and put it into a valid state. If a class does not have a constructor, the CLR will create one by default.

  • While the original .asmx file has the Stock and StockHistory classes, followed by the GetHistory method, the proxy class goes directly to GetHistory. Again, the proxy does not need the first two classes, since the proxy only substitutes for method calls.

  • While the original .asmx file has the public method GetHistory, the proxy class has that method plus two additional, related public methods, BeginGetHistory and EndGetHistory. In fact, you will notice that every web method in the original .asmx file has the same method in the proxy class, plus two others, one for Begin... and another for End.... These additional methods are used to implement asynchronous processing.

Normal method calls are synchronous. In other words, the calling application halts all further processing until the called method returns. If this takes a long time, either because of a slow or intermittent Internet connection (not that that ever happens, of course) or because the method is inherently time-consuming (e.g., a lengthy database query), then the application will appear to hang, waiting.

On the other hand, if the method call is made asynchronously, then the Begin method call is sent out, and processing can continue. When the results come back, the corresponding End method call receives the results. Asynchronous method calls will be demonstrated later in this chapter.

17.2.3 Compiling the Proxy Class

The output of the WSDL utility is a class source code file for the proxy. This source code then must be compiled with the appropriate command-line compiler.

For VB.NET, use the following single command line to compile the proxy:

vbc /out:bin\vbStockTickerProxy.dll /t:library 
    /r:system.dll,system.web.dll,system.web.services.dll,
       system.xml.dll,system.data.dll StockTicker.vb

For C#, use the following single command line:

csc /out:bin\csStockTickerProxy.dll /t:library 
    /r:system.dll,system.web.dll,system.web.services.dll 
    StockTicker.cs

You will notice that although the VB.NET and C# versions of the StockTicker proxy being compiled are functionally identical, with the exact same set of referenced namespaces (using the Imports statement in VB.NET and the using statement in C#) as can be seen by referring back to Example 16-23 and Example 16-24 in Chapter 16, the command-line compile commands are different for the two languages, in that the VB.NET version has two additional namespaces referenced.

This is one of those mysterious, undocumented differences between VB.NET and C#. It turns out that there is a configuration file located in the .NET Framework program directory, called csc.rsp, which contains the list of default references for the C# compiler. There is no comparable configuration file or default list for VB.NET. Presumably the list of default references are hard-coded somewhere.

17.2.4 Automating the Process with a Batch File

Creating the proxy file requires several steps, all performed at a command prompt. Further, several of those steps involve a fair amount of typing of parameters, with lots of places to make mistakes. Finally, when all is done, you probably need to move or copy the resulting dll file to a different directory.

This entire process can be automated somewhat by creating a batch file. Batch files are text files that contain one or more command-line operations. The batch file, which has an extension of .bat, can then be executed from the command line, and all the operations within the file are executed one after the other, just as though they were manually entered at the command line.

Back in the days of DOS, batch files were used extensively. It is possible to make them fairly sophisticated, with replaceable parameters, conditional processing, and other programmatic niceties. For our purposes, a simple batch file will do.

Example 17-3 shows the contents of a batch file that changes to the correct current directory, runs the WSDL utility, compiles the resulting source code, and then copies the resulting dll from one bin directory to another.

Example 17-3. csStockTickerProxy.bat
e:
cd \projects\Programming ASP.NET

rem   Generate the proxy class source file
wsdl /l:CS http://localhost/ProgAspNet/csStockTicker.asmx?wsdl

rem  Compile the proxy class source file
csc /out:bin\csStockTickerProxy.dll /t:library 
    /r:system.dll,system.web.dll,system.web.services.dll 
    StockTicker.cs

rem  Copy the dll
copy bin\csStockTickerProxy.dll 
     c:\inetpub\wwwroot\csWebServiceConsumer1\bin

The first line in the batch file makes drive E the current drive. The next line changes the current directory. Blank lines are ignored. Lines beginning with rem are comments and are also ignored, although the contents are displayed on the screen as the file is processed. After the WSDL utility is run and the resulting file is compiled, it is copied. This last command is equivalent to:

copy e:\projects\Programming ASP.NET\bin\csStockTickerProxy.dll 
     c:\inetpub\wwwroot\csWebServiceConsumer1\bin

Be careful of inadvertent line breaks. A line break in a batch file is the equivalent of hitting the Enter key on the keyboard.

    Previous Section Next Section


    JavaScript Editor Javascript validator     Javascripts 




    ©