Section 16.2. Creating the Web Services Client


16.2. Creating the Web Services Client

In an effort to make its information available to developers and "Amazon Associates" (sites that allow its users to purchase books through Amazon.com), Amazon has created a set of web services. For more information, see http://www.amazon.com/gp/aws/landing.html.

Your application will use these web services, and you'll need to download the Amazon Web Services developer kit for Version 4 (as of this writing). The three things you'll need are an associatesTag (supplied by Amazon), a subscriberID (also supplied by Amazon), and the appropriate .wsdl file (publicly available through the Amazon Web Services pages).

16.2.1. Creating the Amazon proxy

As with all web services accessed through .NET, you must create a proxy class for your client. You do so by obtaining the WSDL document that Amazon supplies, and then compiling that with the command-line instruction:

wsdl /o:Amazon.cs AmazonWebServices.wsdl

16.2.1.1 Creating the desktop application

Create a new desktop application named (for example) AmazonWebServiceClient, and be sure to copy the Amazon.cs file you created from the .wsdl file into that project's directory. Add the file to the project by right-clicking the project and choosing Add Existing Item.

Amazon provides far more information about its books and products than we'll need, so we'll keep it simple and extract only a subset of the information it has available.

In addition, it provides methods that return information about a collection of books, but for now we'll greatly simplify the process (while sacrificing performance) by looking up each book one by one.

To do so you'll create a set of XML files to contain the ISBNs of the books you want to track. I've divided these by technology so that I have a CSharpISBN.xml file, a VBNETISBN.xml file, and an ASPNET_ISBN.xml file.

Example 16-1 shows an excerpt from one of these files.

Example 16-1. CSharpISBN.xml
 <isbns>   <isbn>193183654X</isbn>    <isbn>0130461334</isbn>    <isbn>1893115593</isbn>    <isbn>0130622214</isbn>    <isbn>1861007043</isbn>    <isbn>1861004982</isbn>    <isbn>0672320711</isbn>    <isbn>0596001819</isbn>    <isbn>0735612897</isbn>    <isbn>0735612900</isbn>    <isbn>0596003099</isbn>    <isbn>0596003765</isbn>    <isbn>0072133295</isbn>    <isbn>0672322358</isbn>    <isbn>0072193794</isbn>    <isbn>067232122X</isbn>    <isbn>1588801926</isbn>    <isbn>0672321521</isbn>    <isbn>0735615683</isbn>    <isbn>0201729555</isbn>    </isbns>

The problem, of course, is that these files are often out of date (some books go out of print, new books become available). We'll solve that problem later in this chapter.


As each ISBN is read, the relevant values (title, publisher, rank) are found on the Amazon web site and stored in the Database table. A simple listbox is then updated to indicate progress. Once all the books are recorded, the system becomes dormant while a timer ticks down the remaining time between sessions. You can force a new session by clicking the Now button. This UI was intentionally created to be as simple as possible.

Example 16-2 is the complete desktop application, with analysis to follow.

Example 16-2. SalesRankDBWebServices
#region Using directives using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Windows.Forms; #endregion namespace AmazonWebServiceClient {    partial class AmazonWebServiceClient : Form    {       private int timeRemaining;       const int WaitTime = 900; // 15 minutes       private string connectionString;       private System.Data.SqlClient.SqlConnection connection;       private System.Data.SqlClient.SqlCommand command;       public AmazonWebServiceClient( )       {          InitializeComponent( );       }       private void AmazonWebServiceClient_Load( object sender, EventArgs e )       {          // connection string to connect to the Sales Rank Database          connectionString =     "server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";          // Create connection object, initialize with           // connection string.           connection =             new System.Data.SqlClient.SqlConnection( connectionString );          // Create a SqlCommand object and assign the connection          command =             new System.Data.SqlClient.SqlCommand( );          command.Connection = connection;          timeRemaining = 1;  // when you first start up, get the info.          UpdateButton( );       }       private void btnStart_Click( object sender, EventArgs e )       {          // toggle the timer          updateTimer.Enabled = updateTimer.Enabled ? false : true;          UpdateButton( );       }       private void btnNow_Click( object sender, EventArgs e )       {          timeRemaining = 2;       }       private void UpdateButton( )       {          btnStart.Text = updateTimer.Enabled ? "Stop" : "Start";       }       private void updateTimer_Tick( object sender, EventArgs e )       {          if ( updateTimer.Enabled )             txtClock.Text = ( --timeRemaining ).ToString( ) + " seconds";          else             txtClock.Text = "Stopped";          // hi ho, hi ho, it's off to work we go...          if ( timeRemaining < 1 )          {             timeRemaining = WaitTime;  // reset the clock             // create data set based on xml file             DataSet BookData = new DataSet( );             BookData.ReadXml( "aspnet_isbn.xml" );             // iterate through, calling GetInfoFromISBN for              // each isbn found in file             foreach ( DataRow Book in BookData.Tables[0].Rows )             {                string isbn = Book[0].ToString( );                GetInfoFromISBN( isbn, "ASPNET" );             }             BookData = new DataSet( );             BookData.ReadXml( "csharpIsbn.xml" );             foreach ( DataRow Book in BookData.Tables[0].Rows )             {                string isbn = Book[0].ToString( );                GetInfoFromISBN( isbn, "CSHARP" );             }             BookData = new DataSet( );             BookData.ReadXml( "VBnetIsbn.xml" );             foreach ( DataRow Book in BookData.Tables[0].Rows )             {                string isbn = Book[0].ToString( );                GetInfoFromISBN( isbn, "VBNET" );             }          }       }       private void GetInfoFromISBN( string isbn, string technology )       {          if ( isbn.Length != 10 )             return;                    AWSProductData productData = new AWSProductData( );          ItemLookup lookup = null;          try          {             ItemLookupRequest req = new ItemLookupRequest( );             req.IdType = ItemLookupRequestIdType.ASIN;             req.ItemId = new string[1];             req.ItemId[0] = isbn;             // req.SearchIndex = "Books";             lookup = new ItemLookup( );             lookup.AssociateTag = "libertyassocia00A";             lookup.SubscriptionId = "0SD959SZV6KXV3BKE2R2";             lookup.Request = new ItemLookupRequest[1];             lookup.Request[0] = req;          }          catch ( System.Exception e )          {             lblStatus.Text = e.Message;          }          ItemLookupResponse response;          Items info;          Item[] items;          Item item;          int salesRank = -1;          string author = string.Empty;          string pubDate = string.Empty;           string publisher = string.Empty;           string title = string.Empty;           string strURL = string.Empty;           try          {             response = productData.ItemLookup( lookup );             info = response.Items[0];             items = info.Item;             item = items[0];             salesRank = item.SalesRank == null                ? -1 : Convert.ToInt32(item.SalesRank);             author = FixQuotes( item.ItemAttributes.Author[0] );             pubDate =   FixQuotes(item.ItemAttributes.PublicationDate);             publisher = FixQuotes(item.ItemAttributes.Publisher);             title = FixQuotes(item.ItemAttributes.Title);             strURL = item.DetailPageURL;          }          catch ( System.Exception ex)          {             lblStatus.Text = ex.Message;          }          // update the list box          string results = title + " by " + author + ": " +             publisher + ", " + pubDate + ". Rank: " + salesRank;          lbOutput.Items.Add( results );          lbOutput.SelectedIndex = lbOutput.Items.Count - 1;          // update the database          string commandString = @"Update BookInfo set isbn = '" +             isbn + "', title = '" + title + "', publisher = '" +             publisher + "', pubDate = '" + pubDate + "', rank = " +             salesRank + ", link = '" + strURL + "', lastUpdate = '" +             System.DateTime.Now + "', technology = '" +             technology + "', author = '" +             author + "' where isbn = '" +             isbn + "'";          command.CommandText = commandString;          try          {             // if no rows were affected, this is a new record             connection.Open( );             int numRowsAffected = command.ExecuteNonQuery( );             if ( numRowsAffected == 0 )             {                commandString = @"Insert into BookInfo values ('" +                   isbn + "', '" + title + "', '" + publisher + "', '" +                   pubDate + "', '" + FixQuotes( strURL ) + "', " +                    salesRank + ", '" +                   System.DateTime.Now +                   "', '" + technology + "', '" + author + "')";                command.CommandText = commandString;                command.ExecuteNonQuery( );             }          }          catch ( Exception ex )          {             lblStatus.Text = ex.Message;             lbOutput.Items.Add( "Unable to update database!" );             lbOutput.SelectedIndex = lbOutput.Items.Count - 1;          }          finally          {             connection.Close( );      // clean up          }       }     // close for GetInfoFromISBN       private string FixQuotes( string s )       {          if ( s == null )             return string.Empty;          return s.Replace( "'", "''" );       }    } // end class } // end name space

The program declares a connection string, along with SQLConnection and SQLCommand objects which will be initialized when the form is loaded:

private string connectionString; private System.Data.SqlClient.SqlConnection connection; private System.Data.SqlClient.SqlCommand command;

You can set the Load event by clicking the form and switching from Properties to Events. Double-click the Load event and the skeleton for the load event handler is created for you. Within that event handler, you'll create your connection string (this example uses a trusted connection; you may need to provide a username and password depending on how your database is configured), and the connection and command objects are configured:

private void AmazonWebServiceClient_Load( object sender, EventArgs e ) {    connectionString =      "server=localhost;Trusted_Connection=true;database=AmazonSalesRanks";    connection =       new System.Data.SqlClient.SqlConnection( connectionString );    command =       new System.Data.SqlClient.SqlCommand( );    command.Connection = connection;

The member variable timeRemaining is initialized to one second, and the buttons are updated to set the text on the Start button:

   timeRemaining = 1;  // when you first start up, get the info.    UpdateButton(); }

Each time the timer clicks, the updateTimer_Tick method is called. If the timer is enabled (the user has not clicked Stop), the timeRemaining member variable is decremented, and when it hits 0 it is time to process the books:

if ( updateTimer.Enabled )    txtClock.Text = ( --timeRemaining ).ToString() + " seconds"; else    txtClock.Text = "Stopped"; // hi ho, hi ho, it's off to work we go... if ( timeRemaining < 1 ) {

The first step is to reset the timer to WaitTime (a constant equivalent to 15 minutes) and then to process the .xml files:

timeRemaining = WaitTime;  // reset the clock DataSet BookData = new DataSet(); try {    BookData.ReadXml( "aspnet_isbn.xml" ); }

This creates a dataset, in which each row represents an entry in the XML file. Once the books are read, you extract each ISBN in turn, and call the helper method GetInfoFromISBN, passing in the ISBN and the "technology" under which this ISBN will be stored in the database:

foreach ( DataRow Book in BookData.Tables[0].Rows ) {    string isbn = Book[0].ToString();    GetInfoFromISBN( isbn, "ASPNET" ); }

GetInfoFromISBN is the heart of the program; it is here that you contact the Amazon Web Service.

The first step is to ensure that the length of the ISBN is exactly 10 (a full check would use a regular expression to ensure that the ISBN is 9 integers followed by either an integer or the letter X, and then to perform a checksum on the ISBN [the final digit represents the checksum value], but that is left as an exercise for the reader).

The Amazon.cs file defines a number of useful objects. The ones we'll use for this example include the AWSProductData , the ItemLookup and ItemLookupRequest, as well as the Item objects and collections. Here are the steps:

  1. Declare a new instance of the AWSProductData, which acts as the proxy to the Amazon web service.

  2. Call the ItemLookup method on the AWSProductData instance, passing in a properly initialized instance of ItemLookup.

  3. Get back an ItemLookupResponse object.

  4. Extract the Items array and from that get the first object (offset 0), an object of type Items.

  5. Ask that Items object for its Item property, which is an array of Items objects.

  6. Get the first Item in the array and from that Item, get all the information about the book you've requested.

To make this work, you must first create an instance of ItemLookupRequest and set its IDType property to the enumerated type ItemLookupRequestIdType.ASIN:

ItemLookupRequest req = new ItemLookupRequest(); req.IdType = ItemLookupRequestIdType.ASIN;

Initialize its ItemID array to hold one string, and set that string to the ISBN you are looking for:

req.ItemId = new string[1]; req.ItemId[0] = isbn;

Next, instantiate an ItemLookup object, and set its AssociateTag and SubscriptionID properties:

lookup.AssociateTag = "libertyassocia00A"; lookup.SubscriptionId = "Your ID Here";

Initialize its Request property to be an array of one object, and set that object to the ItemLookupRequest object you created earlier:

lookup.Request = new ItemLookupRequest[1]; lookup.Request[0] = req;

Note that we're using the method-invocation idiom as .NET assumes, but what is really going on is that we're using SOAP to exchange messages with Amazon. We send "tell me about this book" and Amazon returns "here is information about the book."


You're ready to make your request. Do so in a try block to catch any exceptions that might be thrown in the process. Begin by invoking the ItemLookup method:

response = productData.ItemLookup( lookup );

Response should now be non-null. You might add error checking to handle a null response from Amazon (left out here to simplify the code). The Item property returns an object of type Items, which is an array of Item objects. You will extract the first Item object, which will contain information about the book you've requested:

info = response.Items[0]; items = info.Item; item = items[0];

You can now set local variables to hold the values you've retrieved. The FixQuotes method is a helper method to convert single quotes in any string you receive so that they will not cause problems for the database:

salesRank = item.SalesRank == null ? -1 : Convert.ToInt32(item.SalesRank); author = FixQuotes( item.ItemAttributes.Author[0] ); pubDate =   FixQuotes(item.ItemAttributes.PublicationDate); publisher = FixQuotes(item.ItemAttributes.Publisher); title = FixQuotes(item.ItemAttributes.Title); strURL = item.DetailPageURL;

With this information in hand, you are ready to update the listbox and, more important, to update the database.

When updating the database, you'll first try an Update statement. If the number of rows affected is 0, the row doesn't yet exist in the database, so you'll insert the values.

This program would be more secure if it used parameterized queries. The query is left in-line to keep the example simple.


With that done, you're ready to move on to the next ISBN.



Programming C#(c) Building. NET Applications with C#
Programming C#: Building .NET Applications with C#
ISBN: 0596006993
EAN: 2147483647
Year: 2003
Pages: 180
Authors: Jesse Liberty

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