17.3 Creating the Consuming Application

Once the proxy dll is created and placed in the bin subdirectory, then it is a simple matter to create the consuming application. All that is necessary is to add the necessary reference to that dll in the consuming application. This will be demonstrated for a web page created in a text editor and also for a web page created in Visual Studio .NET.

As long as the signatures and return types of the exposed web service methods do not change, the proxy will continue to work. The signature of a web method consists of the name of the method and its parameter list and return value.

The web service can have additional web methods added without breaking the proxy, although the new web methods will not be visible to the consuming application until the proxy source code is regenerated and recompiled. Likewise, existing web methods can have their underlying code modified, but as long as their signature does not change, the proxy will still work fine.

17.3.1 Using a Text Editor

To create a web page that will consume a web service, create a normal ASP.NET web page. Then create a bin subdirectory immediately below the directory containing the .aspx file. Put the compiled proxy dll in the bin directory. Then in the source code of the web page, instantiate the proxy class. This is either done in the script block of the .aspx file, if it is coded inline, or just inside the class definition, if it uses a code-behind class.

Example 17-4 and Example 17-5 show a consuming web page, coded inline, using VB.NET and C#, respectively. Example 17-4 shows the complete .aspx file, while Example 17-5 shows only the script block (since the HTML is identical for both the C# and the VB.NET versions). The resulting web page is shown in Figure 17-1.

Example 17-4. vbStockTickerConsumer.aspx
<%@ Page Language="VB" %> <script runat="server">    dim proxy As new StockTicker(  )    sub txtFirmNameStockSymbol_TextChanged(ByVal Sender as Object, _                                   ByVal e as EventArgs)       lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text)    end sub    sub txtPriceStockSymbol_TextChanged(ByVal Sender as Object, _                                        ByVal e as EventArgs)       lblStockPrice.Text = "$ " & _             Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text))    end sub    sub btnStockExchangeSet_Click(ByVal Sender as Object, _                                  ByVal e as EventArgs)       proxy.SetStockExchange(txtStockExchange.Text)    end sub    sub btnStockExchangeGet_Click(ByVal Sender as Object, _                                  ByVal e as EventArgs)       txtStockExchange.Text = proxy.GetStockExchange(  )    end sub    sub btnGetHistory_Click(ByVal Sender as Object, _                            ByVal e as EventArgs)       dim theStock as Stock = _             proxy.GetHistory(txtHistoryStockSymbol.Text)       dim StockName as string = theStock.StockName       dim StockPrice as double = theStock.Price       dim TradeDate1 as DateTime = theStock.History(0).TradeDate       dim Price1 as double = theStock.History(0).Price       dim TradeDate2 as DateTime = theStock.History(1).TradeDate       dim Price2 as double = theStock.History(1).Price        '  Display the results.        pnlHistory.Visible = true        lblHistoryStockName.Text = StockName        lblHistoryStockPrice.Text = "$ " + Convert.ToString(StockPrice)        lblHistoryDate1.Text =TradeDate1.ToString("d")        lblHistoryPrice1.Text = "$ " + Convert.ToString(Price1)        lblHistoryDate2.Text = TradeDate2.ToString("d")        lblHistoryPrice2.Text = "$ " + Convert.ToString(Price2)    end sub </script> <html>    <body>    <form runat="server">       <h1>StockTicker Web Service Consumer</h1>       <br/>       Firm Name:&nbsp;&nbsp;&nbsp;       <asp:textBox           docEmphBold">txtFirmNameStockSymbol"           OnTextChanged="txtFirmNameStockSymbol_TextChanged"          size="40"           text="Enter stock symbol."          AutoPostBack="true"          runat="server" />       &nbsp;&nbsp;&nbsp;       <asp:label  text="" runat="server"/>       <br/>       Stock Price:&nbsp;&nbsp;&nbsp;       <asp:textBox           docEmphBold">txtPriceStockSymbol"         OnTextChanged="txtPriceStockSymbol_TextChanged"          size="40"           text="Enter stock symbol."        AutoPostBack="true"          runat="server" />       &nbsp;&nbsp;&nbsp;       <asp:label  text="" runat="server"/>       <br/>       StockExchange:&nbsp;&nbsp;&nbsp;       <asp:textBox           docEmphBold">txtStockExchange"           size="40"           text=""        AutoPostBack="false"          runat="server" />       &nbsp;&nbsp;&nbsp;       <asp:button          docEmphBold">btnStockExchangeSet"           text="Set"           onClick="btnStockExchangeSet_Click"           runat="server" />       &nbsp;&nbsp;&nbsp;       <asp:button          docEmphBold">btnStockExchangeGet"           text="Get"           onClick="btnStockExchangeGet_Click"           runat="server" />       <br/>       Stock History:&nbsp;&nbsp;&nbsp;       <asp:textBox           docEmphBold">txtHistoryStockSymbol"           size="40"           text=""          runat="server" />       &nbsp;&nbsp;&nbsp;       <asp:button          docEmphBold">btnGetHistory"           text="Get History"           onClick="btnGetHistory_Click"           runat="server" />    <br/>    <asp:Panel              visible="false"       runat="Server" >          <br/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stock Name:&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>          <br/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          Stock Price:&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>          <br/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;           Transaction 1:&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>          <br/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          Transaction 2:&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          <asp:label  text="" runat="server"/>    </asp:Panel>     </form>    </body> </html>
Example 17-5. Script block from csStockTickerConsumer.aspx
<%@ Page Language="C#" %> <script runat="server">    StockTicker proxy = new StockTicker(  );    void txtFirmNameStockSymbol_TextChanged(Object Source, EventArgs E)    {       lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text);    }    void txtPriceStockSymbol_TextChanged(Object Source, EventArgs E)    {       lblStockPrice.Text = "$ " +           Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text));    }    void btnStockExchangeSet_Click(Object Source, EventArgs E)    {       proxy.SetStockExchange(txtStockExchange.Text);    }    void btnStockExchangeGet_Click(Object Source, EventArgs E)    {       txtStockExchange.Text = proxy.GetStockExchange(  );    }    void btnGetHistory_Click(Object Source, EventArgs E)    {       Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text);       string StockName = theStock.StockName;       double StockPrice = theStock.Price;       DateTime TradeDate1 = theStock.History[0].TradeDate;       double Price1 = theStock.History[0].Price;       DateTime TradeDate2 = theStock.History[1].TradeDate;       double Price2 = theStock.History[1].Price;       //  Display the results.       pnlHistory.Visible = true;       lblHistoryStockName.Text = StockName;       lblHistoryStockPrice.Text = "$ " + Convert.ToString(StockPrice);       lblHistoryDate1.Text =TradeDate1.ToString("d");       lblHistoryPrice1.Text = "$ " + Convert.ToString(Price1);       lblHistoryDate2.Text = TradeDate2.ToString("d");       lblHistoryPrice2.Text = "$ " + Convert.ToString(Price2);    } </script>

In Example 17-4 and Example 17-5, the first line of code inside the script block instantiates the web service proxy class that was recently compiled and placed in the bin directory. By instantiating the proxy here, the proxy variable can be used in any of the code that consumes the web service.

Figure 17-1. Web service consumer web page
figs/pan2_1701.gif

The HTML portion of Example 17-4 and Example 17-5 provides only the minimum user input necessary to allow demonstration of the principles. There are four textboxes, with the following names: txtFirmNameStockSymbol, txtPriceStockSymbol, txtStockExchange, and txtHistoryStockSymbol. The first two have their AutoPostBack property set to true. Therefore, as soon as the value in those text boxes changes, it will fire the onTextChanged event, which will cause the designated event handler to execute. Each event handler makes a call to the relevant proxy method and displays the returned value in a label next to the text box.

The txtStockExchange text box does not do anything when the value in the text box changes. However, it has two buttons associated with it. The Set button sets an application variable with the contents of the txtStockExchange text box, while the Get button fills the txtStockExchange text box with the contents of the application variable.

The txtHistoryStockSymbol text box also does not do anything when the value in the text box changes. It has the btnGetHistory button associated with it. When that button is clicked, the btnGetHistory_Click event handler is called. This method demonstrates how to retrieve class member variables from within the web service class.

To retrieve the Stock member variables, you must first instantiate the Stock class. Looking back at Example 16-17 and Example 16-18 in Chapter 16, you will recall that the GetHistory web method returns an object of type Stock. The Stock object is instantiated here in the event handler with the following line of code in VB.NET:

dim theStock as Stock = proxy.GetHistory(txtHistoryStockSymbol.Text)

In C#, it is done with this line of code:

Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text);

Once the class is instantiated, it is a simple matter to assign its member variables to local variables in the event handler using dot notation. In VB.NET, it looks like this:

dim StockName as string = theStock.StockName

In C#, it looks like this:

string StockName = theStock.StockName;

Accessing the array variables contained in the StockHistory class contained within the Stock class is similar. In VB.NET, the code is this:

dim TradeDate1 as DateTime = theStock.History(0).TradeDate

In C#, the code is this:

DateTime TradeDate1 = theStock.History[0].TradeDate;

Recall that array indices are zero-based.

To repeat a point made in Chapter 16 when the web service example was first created, a real-world application would not store this type of history data in an array, nor would it display the history data in fixed variables as shown here. More likely, you would have the data in a collection or a dataset and display the data in some sort of data-bound control, as described in Chapter 9.

To display the history, several labels contained within an ASP panel control are used. The panel is used to control the labels' visibility. When the page is originally designed, the panel has its visibility property set to false. When it is time to display the results in the button event handler, the panel then has its visibility property set to true, which also makes all the labels contained within the panel visible.

The important point to understand here is that calls to the web service are made instead to the proxy, as though the web service were a local dll or component. When your code makes a method call to the proxy dll, it has no idea that the call is being satisfied over the Internet by a web service. The proxy hides all the complex stuff required to package the method call up as a SOAP message, send it out over the wire using the proper protocol (typically HTTP), receive the SOAP message response, and return that back to your calling program as though the Internet was never involved.

17.3.2 Using Visual Studio .NET

To make the same web page in Visual Studio .NET, open the program and start a new project. For this demonstration, choose the Visual Basic Project type, using the ASP.NET Web Application template. Name the project vbWebServiceConsumer, as shown in Figure 17-2.

Figure 17-2. New project for web service consumer
figs/pan2_1702.gif

When the new project opens, change the pageLayout property of the document from GridLayout to FlowLayout. This changes the page layout from absolute positioning of the HTML elements to a layout more like a word processor, where positioning is achieved with spaces and line breaks.

Working on the WebForm1.aspx design screen, place controls and lay out the page so that it looks similar to that shown in Figure 17-3.

Figure 17-3. Page layout for web service consumer
figs/pan2_1703.gif

Name the text boxes, the labels to the right of the text boxes and in the panel, the panel itself, and the buttons the same as in Example 17-4 and Example 17-5, for consistency and to clarify the analysis. Be sure to clear the Text values from lblFirmName, lblStockPrice, and the labels in the panel, and set the AutoPostBack property of the first two text boxes (txtFirmNameStockSymbol and txtPriceStockSymbol) to True. Also, set the Visible property of pnlHistory to False.

Add event handlers for the first two text boxes and the buttons. To add an event handler, simply double-click on the control. This will automatically bring you to the code-behind page and insert a subroutine with the default name and declaration. Enter in the lines of code for each event handler from the code in Example 17-4 and Example 17-5.

When you do this, you will notice that Visual Studio .NET underlines proxy in each line. This flags those terms as compile errors waiting to happen. To solve that, enter the line of code at the top of the code-behind class that instantiates the proxy. To repeat a line of code already placed in Example 17-4 and Example 17-5, this will look like this:

dim proxy As new StockTicker(  )

Now the error flag is gone from the proxy terms, but the StockTicker class name is flagged as a compile error. This is because the proxy class is not yet available to the project. If you have not already created and compiled the VB.NET proxy DLL, do so now.

Next comes the magic. Using the Solution Explorer, add a Reference to the DLL you just created. Right-click on References in the Solution Explorer. You are presented with two choices:

  • Add Reference...

  • Add Web Reference...

In this example select the first choice, Add Reference.... Click on the Browse button and browse to the location of the proxy dll, which here is called vbStockTickerProxy.dll. When you are finished, the Solution Explorer will look like Figure 17-4. Once the reference is added, the error flag will disappear from the StockTicker class name.

Figure 17-4. Adding reference to web service consumer
figs/pan2_1704.gif

The project can now be run by pressing F5, clicking the Start icon, or selecting Start under the Debug menu.

If you would rather let Visual Studio .NET create and compile the proxy, then select Add Web Reference... after right-clicking on References in Solution Explorer. You will be presented with the dialog box shown in Figure 17-5.

The Add Web Reference... option also allows you to add a reference from a web service found in a UDDI directory. UDDI was described earlier in the chapter.

Figure 17-5. Adding web reference to web service consumer
figs/pan2_1705.gif

Enter a URL pointing to the web service .asmx file with ?WSDL appended to the end. For this example, the URL entered is:

http://localhost/progaspnet/csStockTicker.asmx?WSDL

The resulting WSDL will be displayed in the left side of the dialog box. The right side will show the available web service and the Add Reference button will become enabled. Click on the Add Reference button. Visual Studio .NET will automatically create the proxy DLL and register it with the project.

Add (or modify, if you have already added the equivalent line above) the following line to the code-behind file. In VB.NET, add:

dim proxy as new localhost.StockTicker(  )

In C#, add:

localhost.StockTicker proxy = new localhost.StockTicker(  );

Also, modify the line instantiating the Stock object in the btnGetHistory event handler. In VB.NET, it should look like this:

dim theStock as localhost.Stock =       proxy.GetHistory(txtHistoryStockSymbol.Text)

In C#, it should look like this:

localhost.Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text);

17.3.3 Using Asynchronous Method Calls

As mentioned previously in Section 17-2, web services allow the developer to call any of the exposed web methods either synchronously or asynchronously.

When a method is called synchronously, which is the "normal" way of doing method calls, the program execution waits for the method to return. As long as the method does not take too long to process and there is not too much network delay, this pause is not a problem.

Figure 17-6 shows synchronous processing. Methods are called on the server via the proxy. The calling program is not aware that a proxy is intervening in the process. A call goes out and when the results come back, the calling program continues processing.

Figure 17-6. Synchronous method calls
figs/pan2_1706.gif

However, in situations where the method is time-consuming to process (for example, a lengthy database operation or extensive computation) or where the network delay is significant, then this delay can be an unacceptable performance hit. In the case of web services, where all the method calls entail a round trip over the Internet, long network delays are common. Broadband Internet connections can help, but performance will still suffer.

One solution is to use asynchronous processing. In this model, a web service method is called, with instructions to notify the client when the result is ready. The client can go about its business, not waiting for the method to return. When the asynchronous method completes, a callback method is called. The client then retrieves the data from the server. Asynchronous processing is shown schematically in Figure 17-7.

Figure 17-7. Asynchronous method calls
figs/pan2_1707.gif

As with the synchronous method call, the client is not aware that the proxy is intercepting the method call and passing it along to the server. The client event handler calls the Begin... method on the web service (actually on the proxy) passing in a delegate for the callback method (step 1 in Figure 17-7). The client then goes on to do other work.

The proxy calls the web method on behalf of the client (step 2). When the server has completed the method, it returns the result to the proxy (step 3). The proxy calls the client's callback method and passes in an object implementing IAsyncResult (step 4).

The client passes that IAsyncResult back to the proxy's End... method (step 5). The End... method then returns the data to the client (step 6).

The client does not have to poll the server; it is notified by the callback when the method completes.

The callback method is a delegate. A delegate is a reference type that encapsulates a method with a specific signature and return type. The async Begin... and End... methods define a delegate for the callback mechanism you implement in your client.

To illustrate the use of delegates and asynchronous proxy calls, create a new C# web application project named csWebServiceConsumerAsync in Visual Studio .NET.

The page layout should look like Figure 17-8. It is nearly identical to the layout shown in Figure 17-3, including the panel used to control the visibility of the labels it contains. The only difference is that there is no field or buttons for Stock Exchange, the Get History button from Figure 17-3 is now labeled Get Data, and the button ID is now btnGetData. In addition, the AutoPostBack property of the txtFirmNameStockSymbol and txtPriceStockSymbol text boxes should be set to false rather than true.

Figure 17-8. Asynchronous web page layout
figs/pan2_1708.gif

The web page will accept stock symbols in each of the text fields. When the Get Data button is clicked, all the processing for each field will be done asynchronously. If this were a real-world application where each field would typically be hitting a different web service on different servers, this asynchronous processing would prevent one slow connection from holding up the works. All three web service calls will essentially be occurring simultaneously.

Before entering the asynchronous code, you will make the page work synchronously to see how it works. To the end user, the synchronous and asynchronous implementations will look identical, except the latter should be somewhat faster (although that will not be noticeable in this example, where all of the web method calls are actually going to localhost).

Add the single event handler to the Get Data button by double-clicking on the button. This will bring you to the btnGetData_Click event handler in the code-behind page. Enter the code in Example 17-6 to the event handler (or Example 17-7 for VB .NET).

Example 17-6. Synchronous event handler in C#
lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text); lblStockPrice.Text = "$ " +      Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text)); Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text); string StockName = theStock.StockName; double StockPrice = theStock.Price; DateTime TradeDate1 = theStock.History[0].TradeDate; double Price1 = theStock.History[0].Price; DateTime TradeDate2 = theStock.History[1].TradeDate; double Price2 = theStock.History[1].Price; //  Display the results. pnlHistory.Visible = true; lblHistoryStockName.Text = StockName; lblHistoryStockPrice.Text = "$ " + Convert.ToString(StockPrice); lblHistoryDate1.Text =TradeDate1.ToString("d"); lblHistoryPrice1.Text = "$ " + Convert.ToString(Price1); lblHistoryDate2.Text = TradeDate2.ToString("d"); lblHistoryPrice2.Text = "$ " + Convert.ToString(Price2);
Example 17-7. Synchronous event handler in VB .NET
lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text) lblStockPrice.Text = "$ " & _     Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text)) Dim theStock As Stock = proxy.GetHistory(txtHistoryStockSymbol.Text) Dim StockName As String = theStock.StockName Dim StockPrice As Double = theStock.Price Dim TradeDate1 As DateTime = theStock.History(0).TradeDate Dim Price1 As Double = theStock.History(0).Price Dim TradeDate2 As DateTime = theStock.History(1).TradeDate Dim Price2 As Double = theStock.History(1).Price '  Display the results. pnlHistory.Visible = True lblHistoryStockName.Text = StockName lblHistoryStockPrice.Text = "$ " & Convert.ToString(StockPrice) lblHistoryDate1.Text =TradeDate1.ToString("d") lblHistoryPrice1.Text = "$ " & Convert.ToString(Price1) lblHistoryDate2.Text = TradeDate2.ToString("d") lblHistoryPrice2.Text = "$ " & Convert.ToString(Price2)

This code is identical to that in Example 17-5, except it is condensed into a single event handler rather than spread over three different event handlers.

Run the web application, fill in the stock symbols, and press the Get Data button; the resulting web page will look something like Figure 17-9.

Figure 17-9. Synchronous test result
figs/pan2_1709.gif

Before adding the code to convert this web page from synchronous processing to asynchronous processing, examine the proxy class source code shown in Example 17-2. That segment of the source code shows the proxy method calls available for the GetHistory method. There are three of them:

GetHistory

This is the synchronous method. It takes a single parameter, the StockSymbol string.

BeginGetHistory

This method starts the asynchronous processing. It takes three parameters: the StockSymbol string, the delegate callback method of type AsyncCallback, and an object called asyncState.

EndGetHistory

This method takes a single parameter, asyncResult, which is of type IAsyncResult.

Each of the methods exposed in the web service has equivalent Begin... and End... methods to enable asynchronous processing.

The code in Example 17-8 shows the complete code listing for the code-behind page demonstrating asynchronous event handling for a web service consumer. The lines of code relevant to converting the event handling from synchronous to asynchronous are highlighted.

Example 17-8. Asynchronous event handler
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Threading; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace csWebServiceConsumerAsync {    /// <summary>    /// Summary description for WebForm1.    /// </summary>    public class WebForm1 : System.Web.UI.Page    {       protected System.Web.UI.WebControls.Label Label1;       protected System.Web.UI.WebControls.TextBox txtFirmNameStockSymbol;       protected System.Web.UI.WebControls.Label lblFirmName;       protected System.Web.UI.WebControls.TextBox txtPriceStockSymbol;       protected System.Web.UI.WebControls.Label lblStockPrice;       protected System.Web.UI.WebControls.TextBox txtHistoryStockSymbol;       protected System.Web.UI.WebControls.Panel pnlHistory;       protected System.Web.UI.WebControls.Label Label2;       protected System.Web.UI.WebControls.Label lblHistoryStockName;       protected System.Web.UI.WebControls.Label lblHistoryStockPrice;       protected System.Web.UI.WebControls.Label lblHistoryDate1;       protected System.Web.UI.WebControls.Label lblHistoryPrice1;       protected System.Web.UI.WebControls.Label lblHistoryDate2;       protected System.Web.UI.WebControls.Label lblHistoryPrice2;       protected System.Web.UI.WebControls.Button btnGetData;              int flags;           StockTicker proxy = new StockTicker(  );       //  Create delegates.       private AsyncCallback myCallBackFirmNameStockSymbol;       private AsyncCallback myCallBackPriceStockSymbol;       private AsyncCallback myCallBackHistory;           public WebForm1(  )       {          Page.Init += new System.EventHandler(Page_Init);          // assign the call back          myCallBackFirmNameStockSymbol = new                          AsyncCallback(this.onCompletedGetName);          myCallBackPriceStockSymbol = new                          AsyncCallback(this.onCompletedGetPrice);          myCallBackHistory = new                          AsyncCallback(this.onCompletedGetHistory);       }       private void Page_Load(object sender, System.EventArgs e)       {          // Put user code to initialize the page here       }       private void Page_Init(object sender, EventArgs e)       {          //          // CODEGEN: This call is required by the Web Form Designer.          //          InitializeComponent(  );       }       #region Web Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent(  )       {              this.btnGetData.Click += new                            System.EventHandler(this.btnGetData_Click);          this.Load += new System.EventHandler(this.Page_Load);       }       #endregion       private void btnGetData_Click(object sender, System.EventArgs e)       {          flags = 0;          // lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text);          proxy.BeginGetName(txtFirmNameStockSymbol.Text,                             myCallBackFirmNameStockSymbol,                              0);          // lblStockPrice.Text = "$ " +                 Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text));          proxy.BeginGetPrice(txtPriceStockSymbol.Text,                              myCallBackPriceStockSymbol,                               0);                       // Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text);          proxy.BeginGetHistory(txtHistoryStockSymbol.Text,                                myCallBackHistory,                                 0);          while (flags < 3)          {             Thread.Sleep(100);          }       }       private void onCompletedGetName(IAsyncResult asyncResult)       {          string s = proxy.EndGetName(asyncResult);          lblFirmName.Text = s;          flags++;       }       private void onCompletedGetPrice(IAsyncResult asyncResult)       {          lblStockPrice.Text = "$ " +                         Convert.ToString(proxy.EndGetPrice(asyncResult));          flags++;       }       private void onCompletedGetHistory(IAsyncResult asyncResult)       {          Stock theStock = proxy.EndGetHistory(asyncResult);          string StockName = theStock.StockName;          double StockPrice = theStock.Price;          DateTime TradeDate1 = theStock.History[0].TradeDate;          double Price1 = theStock.History[0].Price;                             DateTime TradeDate2 = theStock.History[1].TradeDate;          double Price2 = theStock.History[1].Price;          //  Display the results.          pnlHistory.Visible = true;          lblHistoryStockName.Text = StockName;          lblHistoryStockPrice.Text = "$ " + Convert.ToString(StockPrice);          lblHistoryDate1.Text =TradeDate1.ToString("d");          lblHistoryPrice1.Text = "$ " + Convert.ToString(Price1);          lblHistoryDate2.Text = TradeDate2.ToString("d");          lblHistoryPrice2.Text = "$ " + Convert.ToString(Price2);          flags++;       }          } }

The first step is to declare the delegates. Add the following lines of code to the code-behind page inside the WebForm1 class:

private AsyncCallback myCallBackFirmNameStockSymbol; private AsyncCallback myCallBackPriceStockSymbol; private AsyncCallback myCallBackHistory;

These lines declare the delegates as private members of the class. Note that the delegates are of type AsyncCallback. This is the same type as the second parameter required by the Begin... methods.

An AsyncCallback delegate is declared in the System namespace as follows:

public delegate void AsyncCallback (IAsyncResult ar);

Thus this delegate can be associated with any method that returns void and takes the IAsyncResult interface as a parameter.

You will create three methods in your client to act as callback methods: onCompletedGetName, onCompletedGetPrice, and onCompletedGetHistory. You encapsulate these methods within their delegates in the constructor, as follows:

myCallBackFirmNameStockSymbol = new                 AsyncCallback(this.onCompletedGetName); myCallBackPriceStockSymbol = new AsyncCallback(this.onCompletedGetPrice); myCallBackHistory = new AsyncCallback(this.onCompletedGetHistory);

You will see how to implement the three callback methods shortly.

The next step is to call all the Begin... methods to start the asynchronous processing. Replace each of the lines of code in btnGetData_Click that calls one of the proxy methods with its equivalent Begin... method. (For now, just comment out the original lines of code and keep them for reference.) The first parameter for each Begin... method is the same as the parameter for the original synchronous method. The second parameter is the delegate created previously. The third parameter is an object for maintaining state, if necessary. For this example, use zero. The btnGetData_Click event procedure should appear as follows:

private void btnGetData_Click(object sender, System.EventArgs e) {    flags = 0;    // lblFirmName.Text = proxy.GetName(txtFirmNameStockSymbol.Text);    proxy.BeginGetName(txtFirmNameStockSymbol.Text,                       myCallBackFirmNameStockSymbol,                        0);    // lblStockPrice.Text = "$ " +           Convert.ToString(proxy.GetPrice(txtPriceStockSymbol.Text));    proxy.BeginGetPrice(txtPriceStockSymbol.Text,                        myCallBackPriceStockSymbol,                         0);         // Stock theStock = proxy.GetHistory(txtHistoryStockSymbol.Text);    proxy.BeginGetHistory(txtHistoryStockSymbol.Text,                          myCallBackHistory,                           0);    while (flags < 3)    {       Thread.Sleep(100);    } }

The flags variable and the while loop will be explained shortly.

Create the three callback methods, and move the code from the current btnGetData_Click to the appropriate method. Call the new methods onCompletedGetName, onCompletedGetPrice, and onCompletedGetHistory. The contents of these methods is shown in Example 17-7.

In each of the callback methods, the End.. method associated with the appropriate web method in the proxy is called to construct the label Text properties to be set for display in the web page. In onCompletedGetName, a string is set to the return value from the proxy.EndGetName method. This string is then assigned to the label Text property. onCompletedGetPrice uses a similar technique, using a single line of code to replace the two lines in onCompletedGetName. onCompletedGetHistory is similar, except that it instantiates a Stock object with the return value from proxy.EndGetHistory. As you will recall from Example 17-1, a Stock object contains a stock symbol, stock name, price, and an array of StockHistory objects.

The last thing to explain is the flags variable, which is a counter. Notice that this variable is declared as a member variable:

int flags;

Each one of the callback methods increments the flags counter:

flags++;

Within the button click event handler, btnGetData_Click, the flags counter is reset to zero. Then every callback method increments the counter. The while loop prevents the button click event from completing until all three callback method methods have completed:

while (flags < 3) {    Thread.Sleep(100); }

When the web page is run, the three Begin... methods are called. As each returns results, the onCompleted... methods call the appropriate End method and increment the counter.

When the counter reaches 3, the web page redraws. The end result looks indistinguishable from that shown in Figure 17-9.

Asynchronous consumption of web services can be very useful under the correct circumstances, but may not scale well. This is because each asynchronous method call spawns a new thread. So, Example 17-7 would spawn three additional threads in addition to the main thread. This would be fine for a low-volume web site, but the performance penalty could overwhelm a large, busy web site.



Programming ASP. NET
Programming ASP.NET 3.5
ISBN: 0596529562
EAN: 2147483647
Year: 2003
Pages: 156

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