Chapter 12: Creating an ADO.NET Shopping Cart – ASP.Net Web Developer's Guide

Chapter 12

Creating an ADO.NET Shopping Cart

Introduction

Now that we’ve gotten XML under our belt, let’s start working with ADO.NET. A good way to really see what ADO can do is within the frame of a shopping cart application. In this chapter, we will create a shopping cart application for a fictitious online bookseller called “Book Shop.”

To enable online shoppers to purchase books from our site, our shopping cart application must be able to: authenticate users, show current contents of the cart, and enable add, update, and checkout operations.

We will also need to create a catalog that our shoppers can browse through to add items to their cart. Users should also be able to query books by category and view a range of books at a time. In order to achieve these goals, we will create the following:

 A database to store all book details

 Stored procedures (MS SQL 2000) or parameterized queries (MS Access 2000) for all add, update, delete, and retrieve operations

 Web Services that will handle all database interactions

 Web Services Description Language (WSDL) Web references to our Web Services

 Server-side classes that will connect the Web Services with our user interface (UI)

 Web interface for displaying both our catalog and cart

We will also need to create admin interfaces to handle add, update, delete, and retrieve operations for our customers (site users) and site administrators. The interface that will be created in our example can be seen in Figure 12.1.

Figure 12.1 The “Book Shop” User Interface

Setting Up the Database

First, we will design the database for our shopping cart. We will start out by designing an MS Access 2000 database which we will then upsize to a SQL Server 2000 database.

We are creating what is called a relational database. A relational database is a series of tables that represent entities related to one another. Let’s look at a simple example to help illustrate this point: our database. See Figure 12.2.

Figure 12.2 Table Relationship

NOTE

To set up the database in this example, you will need to know some basic fundamentals of database design. A good source is Syngress Publishing’s Designing SQL Server 2000 Databases for .NET Enterprise Servers.

Table “Books” is an entity that represents all the attributes of a book. Table “Categories” is an entity that represents all the attributes for a specific category. A relationship between the two tables is created by the use of primary and foreign keys. Table “Categories” has an attribute named CAT_ID, which is the primary key for the table. This means simply that CAT_ID uniquely identifies every row in the table. This will ensure we won’t get duplicate rows of data. The same concept is true for the table “Books.” We can create the relationship between the two tables by putting the attribute CAT_ID into the table “Books.” By doing so, we have created a foreign key in the table “Books” which references the table “Categories.” We have now created a one-to-many relationship between the table “Books” and the table “Categories.”

There are three different types of table relationships:

 One-to-one Exactly one row corresponds with a matching row of the related table.

 One-to-many One row corresponds to many rows of the related table.

 Many-to-many Many rows correspond to many rows of the related table.

Warning

A many-to-many relationship between tables is not a recommended practice. When this type of relationship is created in the design of your database, use a splitter table in-between the two tables that have the affected relationship. This will create two one-to-many relationships and ensure data integrity for your database.

We will now create the entities for our shopping cart application. Entities enable us to map the real world. Since we are making a shopping cart, we need some basic objects to start off with. First of all, we need product. We have chosen to use “Books” as the product for the shopping cart but this could be anything. Next, we need an object that will be using the shopping cart, “Customers.” As in the previous paragraph, we have more than one category of product, or in our case “Books,” so we have another object to map which is “Categories.” The last piece to finish off the whole design is a way to track what is bought, “BookOrders.” Now we need to go over each entity to explain why we have selected the attributes included in each.

Setting Up the Table “Books”

The Books table will contain the following attributes:

 BK_ISBN This will also be our Primary key for the table since an ISBN is already a global unique identifier.

 BK_Author This contains the author’s full name.

 BK_Price The price of the book.

 BK_Title The book title.

 BK_Description A brief description of the book.

 BK_ImagePath The path to where we will store the image.

 CAT_ID Our foreign key attribute to table “Categories.”

Setting Up the Table “Categories”

The Categories table will contain the following attributes:

 CAT_ID The primary key for the table which will be an auto generated number; I will cover this in the next two sections.

 CAT_Name The name of the category.

Setting Up the Table “Customer”

The Customer table will contain the following attributes:

 CT_ID The primary key for the table, an auto generated number.

 CT_FirstName Customer first name.

 CT_LastName Customer last name.

 CT_Email Customer e-mail.

 CT_Password Customer password.

Setting Up the Table “Orders”

The Orders table will contain the following attributes:

 OR_ID The primary key for the table, an auto generated number.

 CT_ID This is our foreign key attribute to table “Customers.”

 OR_Date The date of the order.

 OR_ShippedDate The date the order ships.

Setting Up the Table “BookOrders”

The BookOrders table is the split table for the handling of our relationship between the tables “Books” and “Orders.” This table includes the following attributes:

 OR_ID This is our foreign key attribute to table “Orders.” This is also part of the composite Primary key for the table.

 BK_ISBN This is our foreign key attribute to table “Books.” This is the other part of the composite primary key.

 BKOR_Quantity The total of number of books.

 BKOR_Price The total amount of the order.

Now, lets implement this database in Microsoft Access.

NOTE

It is good practice to come up with a naming convention for your database. The naming convention can be anything of your choosing, just make sure you’re consistent throughout your database. A naming convention is a uniformed way to document your code. In our example, OR_denotes the table “Orders.”

Creating an Access Database

To create a database in Microsoft Access, simply navigate to your program files and select the Access icon. The main window will pull up, prompting you to either pick a database from the list of current databases, create a blank database, or use the wizard. See Figure 12.3.

Figure 12.3 Setting Up the Access Database

We want to select the Blank Database option and not the wizard. Select OK, then give the database the name shopDb. Next, select the Tables object. From here, we choose the option Create table in design view. We can now transfer the attributes for the tables into the interface (see Figure 12.4).

Figure 12.4 Creating Tables in Design View

Now we can transfer almost everything that’s been done into the interface. One thing we have not discussed is datatypes.

The following is a list of datatypes we will implement in the database:

 Text Text or combinations of text and numbers: maximum size 255 characters.

 Currency Used for monetary functions, prevents rounding off of total: size 8 bytes.

 AutoNumber Unique number automatically inserted when a record is added: size 4 bytes.

 Number Numeric data to be used for mathematical calculations: size 1, 2, 4, or 8 bytes.

 Date/Time Stores date/time: size 8 bytes.

 Yes/No Boolean value, 0 or 1: size 1bit.

 Memo Used for storing large amounts of text: maximum size 64,000 characters.

 OLE Object Can store Word docs, Excel files, and so on: maximum size 1 gigabyte.

Continue this process for the rest of the tables. If you want, you can load the shopDb.mdb from the CD that accompanies this book, then view the complete database. Let’s look at the complete diagram generated by Access after we finish filling in our tables (shown in Figure 12.5).

Figure 12.5 A Database Diagram

To generate the preceding diagram, go to the Tools menu and select the Relationships option. You will be prompted for what tables to add. Select the tables you have created and hit OK. To create the relationships between the tables, left-click the attribute you want to make a relationship with and drag it over to the table that has the matching attribute, release the mouse and you will be prompted with a set of options for the relationship. See Figure 12.6.

Figure 12.6 Defining Relationships in Access

The default is to have the Enforce Referential Integrity option selected. This is good enough for our example; the other two options will enable cascading deletes and updates.

Warning

When defining relationships, make sure the column is of the same datatype as the one you are trying to make a relationship with, otherwise Access will throw an error.

We will do what is called de-normalize the database for the Access version to make things flow between the Data tier of our application and the two different databases. Since our shopping cart uses all OleDb connections to the database regardless of source, the stored procedures created in the SQL Db are the same for the Access version, but we have some limitations when it comes to Access. We cannot easily return the submitted record ID from the table like we can in SQL using the global variable @@identity, so we must solve this by eliminating the Orders table in the schema for Access and adding those rows to the BookOrders table. This will result in customers having multiple order entries, but keeps all data handling code the same for both databases. If you were to program this application, you would select one or the other and optimize accordingly—we are going to straddle the fence here and show both in the same logic.

Now that we have our database schema done, we can upsize the database using the Access Upsizing Wizard and make a SQL server version. Go to Tools, select Database Utilities, then select Upsizing Wizard. Follow the wizard and choose all the defaults.

SQL Server Database

Now that we have our schema upsized into SQL, we can easily create the rest of our database components. We primarily need a set of stored procedures that will run all of our operations against the database. This will enable us not to have to use ad hoc queries in our code for our Data Tier interaction.

One thing we need to do first is ensure that all our primary keys were transcribed into the upsized version. Let’s open up the Enterprise Manager of MSSQL 2000 (EM). Navigate to your program files and select the SQL Server group, then select EM. From EM, we can quickly navigate to our database (shopDB). See Figure 12.7.

Figure 12.7 The SQL EM Interface

Select Tables and you’ll notice our tables from Access are now here. Right-click a table and select Design Table. From here, we can check to see if our tables made the move without ill effects. If everything looks correct, check the rest of the tables—you’ll see the Access datatype “autonumber” does not come over to SQL Server as an “int” identity column datatype, which it needs to be. So, for the tables that have autonumber, you will have to change it to the “int” datatype with identity, and give them a seed and increment value. See Figure 12.8.

Figure 12.8 Setting Identity to Yes and Giving Seed and Increment Value

You must also uncheck Allow Nulls. This is because the field we are working with is a primary key and we cannot have a null value for a primary key field. Also, we are using the option identity in this instance, which requires that null not be allowed.

We will also separate the table “BookOrders” into its original design since SQL Server can easily give us a value for the identity field returned. After we have done all of this, we can create a new diagram in SQL and apply our new relationships. In the EM view, right-click diagrams and select New Diagram. The wizard will prompt you for the tables you want to select for the database diagram. Add only the tables we have created, leave out all the system tables. We will now view our new diagram generated by SQL Server (see Figure 12.9).

Figure 12.9 A SQL Server Diagram

We can create relationships in the same manner as before. Click the column you want to make a relationship with and drag and drop it into to the appropriate column and table. We will go with the selected defaults. We have a normalized database now completed in SQL Server. We will now create the stored procedures (procs) we’ll need for the rest of the application.

Creating the Stored Procedures

We’ll now create the following list of stored procedures:

 AdminAddBook

 AdminAddCustomer

 AdminAddCat

 AdminDeleteCat

 AdminDeleteCustomer

 AdminDeleteBook

 AdminUpdateBook

 AdminUpdateCat

 AdminUpdateCustomer

 AllCustByld

 GetAllBooks

 GetAllCat

 LoginCustomers

 OrderBook

Don’t be intimidated. We’ll use the SQL Server Wizard to create most of these procedures. Now we need to begin creating all our stored procedures. Go to the Tools menu and select Wizards. From there, a new window will pop up with a listing of items. Double-click the first item, Database, then select Create Stored Procedure Wizard. You should see the screen shown in Figure 12.10.

Figure 12.10 The Create Stored Procedure Wizard

Click Next and select the database, which is shopDb. The next window will show all the tables on the left and the subsequent procedures that can be created on the right. Mark the check box labeled insert in the row of options listed for the Customers table. Click Next. The window that appears will give you the choice to edit the SQL syntax—select this option. We need to give the procedure a name, which in this case will be AdminAddCustomer. See Figure 12.11.

Figure 12.11 The Stored Procedure Wizard’s Properties Dialog Box

In Figure 12.11, we see that all columns are selected for insert; however, we do not need one for CT_ID because the identity field generates that. Uncheck that option and rename the proc AdminAddCustomer. Select Edit SQL. Let’s look at the code generated by this; it’s shown in Figure 12.12 and found on the CD as ShopDB.sql.

Figure 12.12 ShopDB.sql

Here we have the SQL syntax to insert a row of new data. In the code view window, SQL Server likes to put numbers on all the variables. You can delete this so the code looks cleaner and will be easy to use when we write the Web service that will hit this proc and execute it. Create the rest of the Admin procs in this same manner.

Now that we have completed a majority of the stored procedures needed for our database through the use of the wizards, we have to create more complex stored procedures using the Query Analyzer. Open up Query Analyzer from the Tools menu of EM. Connect the server you are running. In the drop-down menu, select the database shopDb. The next proc we need to build is AllCustById. We will write a simple select statement with one parameter.

Let’s look at some code which can be executed in Query Analyzer:

The next procedure in the list after AllCustById is GetAllBooks. No need for parameters—just give up the data.

ORDER BY “name”

NOTE

In the code in this section, we are using aliasing so the column headers returned will have easy-to-use names. The DataSet will use the column names as XML element names when the data is converted to XML.

Now we need to get a selection of categories from the database for our dropdown menus:

CREATE PROC GetAllCat

AS

SELECT * FROM Categories

This will populate with all category names and associated IDs. Now we need to create a proc that will query the database and return a Customer’s ID. This is our Login proc:

This will return a value of either the Customers ID or −1, which we can check for on the page load.

Now we need to handle the ordering of a book. We can load and run the OrderBook procedure to do that:

We are using begin tran and end tran. This simply means that if there is an error during any part of the previous query the transaction will be aborted and rolled back. That’s it for the stored procedures. Now to make these all work in the Access DB, we need to trim out some stuff from the preceding code.

As a rule of thumb, we can grab all the code after the key word AS. This is then pasted into Access query SQL mode and saved as the same file name. Open up the shopDB.mdb file and see the differences in the code.

Creating the Web Services

This section will provide an overview of the Web Services needed for our site, and describe the processes of creating the data connection, creating a Web Service, and, finally, testing the Web Service.

Overview of the Book Shop Web Services

We will be using Web Service methods to wrap our database logic (stored procedures for SQL, or parameterized queries for Access). This will provide separation of the data tier from the UI. This will also enable our data to be accessed from multiple clients including Java-servlets, JSP, PHP, desktop application with Hypertext Transfer Protocol (HTTP) connections, and, of course, ASP.NET applications.

We will be creating the following Web Services (see Figure 12.13):

Figure 12.13 An Overview of Web Services and Their Methods

 sellerAdmin

 adminCustomer

 getCustomer

 loginCustomer

 getBooks

 getCategories

 orderBooks

Earlier in this chapter (see the section “Setting Up the Database”), we created stored procedures for use with an SQL database, as well as the equivalent parameterized queries for use with an Access database, to make the interface to the data source consistent; this allows us to write ADO.NET code that can be used against both SQL and Access.

We will also use the OleDb data connection object since most databases have an OleDb provider. This will enable our code not only to work with SQL and Access but with any database that has an OleDb interface. So, our application will work with an SQL database and our application will work with an Access database. And the only code that will need to be changed with this approach is the connection string.

Let’s create a new project to host all our Web Services. Open Visual Studio .NET Beta 2 (VS.NET), and select New Project. We want to create a C# ASP.NET Web Service application named “booksource” (see Figure 12.14); next, we will create the data connection.

Figure 12.14 Creating the Booksource Web Service

Creating the Data Connection

Data Connections can be created in several ways. Let’s look at how the VS.NET Wizard does this. For this example, we’ll create a connection to an Access database. The steps for MS SQL will be slightly different.

1. Open the Server Explorer, and select View | Server Explorer from the menu.

2. Right-click Data Connection, then select Add connection.

3. Select the Provider tab.

4. Select the appropriate provider. For access, select Jet 4.0 OLEDB Provider.

5. Click Next.

6. Select the database name by clicking the Browse … button and navigating to your database.

7. Click Test Connection. You should get a pop-up window that says Connection succeeded.

8. Click OK.

9. Click OK. You now have a data connection.

While in design mode, you can drag and drop this connection onto your .asmx page. This will add the following to our code-behind page as the first line in the service public class:

private System.Data.OleDb.OleDbConnection oleDbConnection1;

Connection string information will also be added to the InitializeComponent() method. Alternatively, we can still create a connection string by creating a .udl file on the desktop, double-clicking it and following the dialogs. With this method, we will have to insert the code ourselves, as follows:

1. In C#, add Using System.Data.OleDb to the top “using” section.

2. Then add the following inside the service class:

    private OleDbConnection myConnection = new OleDbConnection();

3. Add the following to a method (Page_onload, or a method of your own creation):

    myConnection.ConnectionString =

    [the string obtained from the udl file]

We will take a closer look at adding a connection when we create the “sellerAdmin” service in the next section.

Creating a Web Service

All of the code for the Web Services in this chapter can be found on the CD. (See adminCustomer.asmx.cs, sellerAdmin.asmx.cs, getBooks.asmx.cs, getCategories.asmx.cs, getCustomer.asmx.cs, loginCustomer.asmx.cs, orderBooks.asmx.cs, and sellerAdmin.asmx.cs.)

Let’s take a closer look at adding a connection by creating the “sellerAdmin” Service. To create this service follow these steps:

1. Create the connection object.

2. Set the connection string.

3. Create the Command object.

4. Create the Parameter objects and assign their values.

5. Execute the procedure. We will be using the AdminAddBook proc. It takes the following parameters: BK_ISBN, BK_Author, BK_Price, BK_Title, BK_Description, CAT_ID, BK_ImagePath.

6. Return string indicating success or failure of the operation.

Now let’s get started. To accomplish Step 1 (creating the connection object), first create a new C# Web Service and name it sellerAdmin.asmx. Add this directive to the top “using” section:

Using System.Data.OleDb;

Scroll down to below the method named Dispose(bool disposing). Add the following:

protected OleDbConnection sellerAdminConn = new OleDbConnection();

This accomplishes the creation of the connection object. Now, for Step 2 (setting the connection string), add the following:

Note that the use of the “@” before the connection string is required. This accomplishes Step 2.

For Step 3, (creating the Command object), first create a new method called addItem. It should have parameters corresponding to the stored procedures parameters, and should return a string indicating success or failure of the operation:

public string addItem(string ISBN, string author, double price, string

title, string description, string imagePath, int CAT_ID)

Now create a Command object that references the AdminAddBook proc:

OleDbCommand addItem =

new OleDbCommand(“AdminAddBook”,this.sellerAdminConn);

addItem.CommandType = CommandType.StoredProcedure;

This accomplishes Step 3.

For Step 4 (creating the Parameter objects and assigning their value), we will create Parameter objects for ISBN, author, price, title, description, imagePath, and CAT_ID, then set their values. Here is the code for “isbn”:

OleDbParameter addISBN =

addItem.Parameters.Add(“@BK_ISBN”,OleDbType.Char,15);

addISBN.Value = ISBN;

Note that “@BK_ISBN” is the name of the parameter we are assigning a value to; “OleDbType.Char” is its datatype (it should be compatible with the field in the database); and “15” refers to the character size as defined for the field in the database.

The code to create Parameter objects for each of the method parameters is nearly identical, and can be found on the CD (see: sellerAdmin.asmx.cs). This accomplishes Step 4.

Now, for Step 5 (executing the procedure), we will open the connection and execute the query. Since the stored procedure performs an insert operation it will return an “int” containing the number of rows affected. Therefore, we will use the command ExecuteNonQuery.

this.sellerAdminConn.Open();

int queryResult = QueryObject.ExecuteNonQuery();

This accomplishes Step 5. Now close the connection and return the result of executing the proc (this is Step 6). Note that our method returns the following string: “success” or the generated error message.

This accomplishes Step 6. Since all of the Web methods have similar logic, we can combine some of this code into a method that each Web method calls:

We need to add one more thing to our method to make it accessible as a Web method:

[WebMethod(Description=“Adds a new book to the books table”,

EnableSession=false)]

Putting it all together, we get the following:

In this section, we created the sellerAdmin Web Service and the additem Web Service method. In the next section, we will look at how to test the Web Service and its methods.

Testing a Web Service

We can test our service by performing the following steps:

1. In VS.NET right-click the .asmx file (sellerAdmin.asmx), and select Set as start page.

2. Press F5 to run it. This will take a few seconds to compile and run.

3. When the browser loads, you should see something like Figure 12.15.

Figure 12.15 Web Service Listing

4. To test the service addItem, click its link. An input form will be displayed, prompting you for values for its parameters. See Figure 12.16.

Figure 12.16 Testing a Web Service

5. Fill in the appropriate textboxes and click Invoke.

6. Since this service returns a datatype string, we should see something like Figure 12.17.

Figure 12.17 Results of invoking the addltem Web Service

This shows that the method has completed successfully and returned the corresponding output. These steps can be repeated for each of the remaining methods: removeItem, updateItem, addCat, removeCat, and updateCat. Each of these methods is coupled with a corresponding stored procedure (MSSQL) or parameterized query (MS Access).

The following is a function prototype overview of the process-flow or steps involved in creating each of these Web methods. See if you can create and test these Web methods on your own, then compare them to the source on the CD. The sellerAdmin Web service and all of its methods can be found on the CD (see sellerAdmin.asmx.cs).

 removeItem (int isbn) Removes a book item from the database.

1. Call init().

2. Create Command object accessing the AdminRemoveBook proc.

3. Create the Parameter object and assign its value.

4. Execute the procedure. Call ExecuteQuery(commandObj).

5. Return string indicating success or failure of the operation.

 updateItem (string ISBN, string author, double price, string title, string description, string imagePath, int CAT_ID) Updates a book item’s information.

1. Call init().

2. Create Command object accessing the AdminUpdateBook proc.

3. Create the Parameter objects and assign their values.

4. Execute the procedure. Call ExecuteQuery (commandObj).

5. Return string indicating success or failure of the operation.

 addCat (string CAT_Name) Adds a category name to the database.

1. Call init().

2. Create Command object accessing the AdminAddCat proc.

3. Create the Parameter object and assign its value.

4. Execute the procedure. Call ExecuteQuery(commandObj).

5. Return string indicating success or failure of the operation.

 updateCat (int CAT_ID, string CAT_Name) Updates category details.

1. Call init().

2. Create Command object accessing the AdminUpdateCat proc.

3. Create the Parameter objects and assign their values.

4. Execute the procedure. Call ExecuteQuery(commandObj).

5. Return string indicating success or failure of the operation.

 removeCat (int CAT_ID) Removes a category from the database.

1. Call init().

2. Create Command object accessing the AdminUpdateCat proc.

3. Create the Parameter object and assign its value.

4. Execute the procedure. Call ExecuteQuery(commandObj).

5. Return string indicating success or failure of the operation.

NOTE

This application contains several different Web Services. The code for these Web Services can be found on the CD. (See adminCustomer.asmx.cs, sellerAdmin.asmx.cs, getBooks.asmx.cs, getCategories.asmx.cs, getCustomer.asmx.cs, loginCustomer.asmx.cs, orderBooks.asmx.cs, and sellerAdmin.asmx.cs.)

Now that we know the Web Service and its methods are working correctly, the next step will be to create our UI for the Web application and generate proxy classes for it to retrieve data from our Web Services. In the next section, we will see how VS.NET works with WSDL and Universal Description, Discovery, and Integration (UDDI) to enable our ASP.NET Web Application to connect to and retrieve data from our booksource Web Service project.

Using WSDL Web References

We will use WSDL and disco in our Web application project to connect to and add a reference to our Web Services Application (bookSource) and its individual Web Services and their Web methods. To learn more about WSDL, disco, and Web Services, please see the discussion of this topic in Chapter 10.

Let’s create a new C# Web application, named “bookSourceUI.” The first thing we want to do is create a reference to our Web Services so that we can easily access the methods in our code.

1. In the Solution Explorer pane, right-click Web References.

2. Select Add Web Reference. A new dialog will appear.

3. Select the last UDDI option, which is your local machine.VS.NET will check your server for all Web Services. It will then present you with a list of services you can view or select to add a reference to. See Figure 12.18.

Figure 12.18 UDDI Server Discovery Dialog

4. Select the service group you would like to add a reference to. Look for your Web Service project name (http://localhost/bookSource.vsdisco).

5. The Services available will be displayed. See Figure 12.19.

Figure 12.19 Services Available

6. You can view the Simple Object Access Protocol (SOAP) contracts and documentation for each Service method by clicking on the link. Be sure to add the reference from this level in the menu. To add this Web Service and all its methods, click Add Reference.VS.NET will create proxy classes for each Service method so that the method can be accessed just like a local class method. See Figure 12.20.

Figure 12.20 Proxy Classes Added to Solution Explorer in VSNET UI

Building the Site

Now that the backend database interfaces and Web Services have been completed, we will turn our focus to the middle tier data classes and controls that act as a bridge between the backend and the Web UI. Our site structure will look something like that depicted in Figure 12.21.

Figure 12.21 BookShop Site Overview

Site Administration

In this section, we will develop the code that allows us to tie our site administration interface to our Web Services (see Figure 12.22). While creating the pages needed, we will cover creating the Administration login, creating the Administration page, and an addBook page for the administrator.

Figure 12.22 Site Administration Page Group Overview

Creating the Administration Login (adminLogin.aspx)

This is a fairly simple page that uses the asp:RequiredFieldValidator server control. There are several server controls that enable HTML form validation:

 RequiredFieldValidator

 CompareValidator

 RangeValidator

 RegularExpressionValidator

 CustomValidator

 ValidationSummary

All of these controls work in a similar fashion. In this example page, we use RequiredFieldValidator in a code behind page to show how to use a server control to validate user data in HTML forms.

1. In the Web application bookSourceUI, create a new aspx page, and name it adminLogin.aspx.

2. In Design view, drag and drop a RequiredFieldValidator.

3. Be sure not to position this element in Design view; in the aspx page, remove the style attribute from the element and use HTML layout techniques to position it. (See the sidebar in this section on 28ASP.NET and Netscape.)

Let’s look at the code from the .aspx page:

Lets look at a code snippet from the code-behind file (the aspx.cs page). When we drag the RequiredFieldValidator onto the page, VS.NET will add the following:

protected System.Web.UI.WebControls.RequiredFieldValidator passUValid;

And that’s all there is to it. When the page is run, a reference is made to a client-side JavaScript file that includes crossbrowser code to ensure that this field contains a value before allowing a “submit.” If the user tries to submit without filling in the text box, the error message “You must supply a user name” will appear in the table cell to the right of the text box (it actually appears wherever the asp:requiredfieldvalidator tag is placed in the HTML, in this case an adjacent table cell). Next, we will look at the admin page itself.

Debugging …

ASP.NET Server Controls Do Not Display Correctly in Netscape 4.x

A lot has happened over the last few years with Netscape and the open source Mozilla project. While the newer versions of Mozilla version .094 and later should handle this fine, there is still a significant Netscape 4.x user base. When we develop Web front-ends for our clients, we strive to ensure at least Netscape 4.72 will display and function correctly.

What’s the issueIt seems that most of the examples showing you how to use server controls have you drag and drop the control to where you want it on the screen. In HTML, this creates span tags with inline style attributes containing “absolute positioning.” Those of us that have dealt with cross-browser Dynamic HTML (DHTML) issues know that this can cause problems in Netscape. The solution: Use “FlowLayout” and good old-fashioned HTML elements and tricks for positioning. To do this, simply right-click a page in either “Design” or “HTML” view and switch the pageLayout property to FlowLayout.

Creating the Administrator Page (adminPage.aspx)

The purpose of this page is to allow the site administrator the ability to remove and update book item information. In the following sections, we’ll look specifically at retrieving the data, displaying the data, adding new books to the database, deleting books, and updating book details.

Retrieving the Data: Creating the getBooks.AllBooks Web Method

To retrieve the list of books stored in the database, we will need to access the “GetAllBooks” stored procedure (MSSQL) or parameterized query (MS Access). We will do this by creating the allBooks method of the getBooks Web Service. This method will take no parameters, and will return a DataSet containing all Book data as well as the table structure of the Database table that the data originated from. The Web method getBooks.AllBooks can be found on the CD that accompanies this book (see getBooks.asmx.cs).

1. To create this method, we must first create a new Web Service named “getBooks”. (See the section on Web Services earlier in this chapter.)

2. Inside the code-behind page of getBooks (getbooks.asmx.cs), we need to create the method allBooks. AllBooks should return a DataSet:

    public DataSet AllBooks()

3. Set the connection string:

    string source = “Provider=SQLOLEDB.1;Persist Security Info=False …

4. Create the Connection object:

    OleDbConnection conn = new OleDbConnection (source);

5. Create the Command object accessing the “GetAllBooks” proc:

    OleDbCommand cmd = new OleDbCommand (“GetAllBooks”, conn);

    cmd.CommandType = CommandType.StoredProcedure;

6. Create a DataAdapter object for the Command object:

    OleDbDataAdapter da = new OleDbDataAdapter (cmd);

7. Create a new DataSet and use the DataAdapter to fill it from the results of executing the stored procedure:

    DataSet ds = new DataSet ();

    da.Fill (ds, “Books”);

8. Close the connection and return the DataSet:

    conn.Close();

    return ds;

Here is the method in its entirety:

The data returned contains an embedded xsd schema describing the Database table “Books”.

The next section is the diffgram node, which contains all the table records:

This XML file is interpreted by ASP.NET as a DataSet object and can be easily loaded into any variable of type DataSet. The DataGrid control is designed to be DataBinded to a DataSet object. This makes it easy to “data bind” to a Web Service method that returns a DataSet. Data Binding a DataSet to the DataGrid is almost the same as loading the DataSet into the DataGrid. The DataGrid is then able to iterate through and perform operations on the DataSet as if it were an Access Form connected to an Access database. The DataSet in actuality is an in-memory XML representation of the database including the Books table.

Displaying the Data: Binding a DataGrid to the DataSet

The DataGrid is actually bound to the DataTable Books which is a table within the DataSet returned by getBooks.AllBooks. We create a DataView of the Books table so that we can sort the data. This DataView is then bound to the DataGrid.

In the following code, changeBooks is the name of our DataGrid object:

Dt = Books.AllBooks().Tables[“Books”];

myView = new DataView(Dt);

myView.Sort = “isbn”;

changeBooks.DataSource = myView;

changeBooks.DataBind();

Adding New Books to the Database: Creating the allBooks.addItem Web Method

The creation of this method was shown as an example earlier in the chapter, under the section “Web Services.”

Deleting Books: Deleting from the DataGrid and the Database

Using the DataGrid event changeBooks_DeleteCommand, fired when a user clicks the Delete button in the DataGrid UI, we will select the row in the DataGrid to remove by using the RowFilter property. The following code selects the individual book by performing a filter on ISBN. It is analogous to the SQL statement:

Select * from Books where isbn = “@isbn”

The equivalent code for the DataView is:

myView.RowFilter = “isbn=‘”+upISBN+“’”;

This will return an array or collection of items. Since ISBN is our primary key in the Books table, we know that this filter will return only one item. We delete this row from the DataView by simply calling the Delete method:

myView.Delete(0);

Next, we reset the filter so we can re-access the entire Books table:

myView.RowFilter = “”;

Now we need to resync the DataGrid with the in-memory Books Table View so that the DataGrid UI reflects the change:

changeBooks.DataSource = myView;

changeBooks.DataBind();

Next, we need to update the database to sync it with the DataGrid. This is accomplished by calling the Web method and passing it the ISBN of the book to delete:

removeBook.removeItem(upISBN);

Updating Book Details: Updating the DataGrid and the Database

Using the DataGrid event changeBooks_UpdateCommand, fired when a user clicks the Update button in the DataGrid UI, we will select the row in the DataGrid to update by using the RowFilter property.

1. Select the row to update by using the RowFilter property of the DataView (see the example in the preceding section).

2. Create a new DataRow Item and populate it with the changes (new Data). Store updated column values in local variables:

3. Delete the row that is being updated (see the example in the preceding section).

4. Create a new DataRow and populate it with the new data.

5. Resync the DataGrid with the DataView (see the example in the preceding section).

To update the database, simply call the Web method sellerAdmin.updateItem, passing it the new data.

localhost.sellerAdmin newData = new localhost.sellerAdmin();

newData.updateItem(upISBN, upAuthor, upPrice, upTitle, upDescription,

upImage, upCatId);

One limitation of the DataGrid is that it doesn’t provide a UI for adding new records. We will handle this case by creating another page: addBook.aspx.

Creating the addBook Page (addBook.aspx)

The addBook is another fairly straightforward page. It provides a UI where the site administrator can fill out a simple HTML form and submits. This data is handled by the code-behind page addBook.asmx.cs. This page simply passes the data to the database via the Web method sellerAdmin.addBook:

addNewBook = new localhost.sellerAdmin();

resultAdd =

addNewBook.addItem(addISBN, addAuthor, addPrice, addTitle, addDescription,

addPath, addCatId);

Customer Administration

In this section, we will develop the code that allows us to tie our customer administration interface to our Web Services (see Figure 12.23).

Figure 12.23 Customer Administration Page Group Overview

Creating the Customer Admin Section

This section of the site deals with user authentication, including creating a customer account and login. We use this to simulate order processing.

Creating the loginCustomer Page

We will use the same form layout as we did for the admin login described in the preceding section. One change we’ll implement is that we’ll call a Web Service to verify the login of the customer.

1. Make a call to the Web Service loginCustomer. This should be routine by now, but let’s look at the code to call the Web Service:

    loggedCust = new WebReference1.loginCustomer();

2. Access the Web method validCustomer. Now we have access to all the methods contained in the class.

    string resultId =

    loggedCust.validCustomer(validEmail, validPassword);

3. Return a value. We can now check the value of the variable resultId and either grant the customer access or return an error message.

Now we have the customer logged in to the site and they can go to any page without having to sign in again.

NOTE

We are using a session variable to track where the user is coming from when they are prompted to login. This will enable us to redirect them back to the page where they came from rather then sending them to some nonspecific page and having them navigate through the site from scratch.

Creating the updateCustomerInfo Page

We can now add a page that will let the customer update his or her information. This will be done identically to the example from site admin where we brought in all books and then enabled the site administrator to go through the books listed and delete, update, or add books at will. In this case, we will enable the customer to update only.

1. Select the row to update by using the RowFilter property of the DataView.

2. Create a new DataRow Item and populate it with the changes (new Data).

3. Delete the row that is being updated.

4. Insert the new DataRow.

5. Resync the DataGrid with the DataView.

All five steps are the same as covered in earlier examples. Let’s look at the code one more time:

Dt = Customers.AllCustById((int)Session[“userId”]).Tables[“Customers”];

myView = new DataView(Dt);

myView.Sort = “CT_ID”;

Set the DataTable value into the DataView:

custGrid.DataSource = myView;

custGrid.DataBind();

Set the data source of DataGrid:

Delete the bad data row and the new one:

WebReference1.adminCustomer newData = new

WebReference1.adminCustomer();

newData.updateCust(upId, upFName, upLName, upEmail, upPassword);

Lastly, update the database by calling the Web service.

In the previous examples we have made extensive use of the DataGrid control for DataBinding DataSet information to the UI. We must admit we were a bit reluctant to use the DataGrid since it seemed reminiscent to the DataGrid Design Time Controls (DTCs). DTCs were included with many versions of FrontPage, Visual InterDev, and Office. They made it easy for novice developers to quickly create data driven Web sites. Lets just say DTCs had some drawbacks, to put it politely! In the next two sections, ADOCatalog and XMLCart, we will use XSL/Transforms against XML data to produce our UI. This is accomplished by using the asp:xml server control as well as client side script and hidden asp:text controls. The ADOCatalog’s primary interfaces will return DataSet objects so it could be easily tied to a DataGrid control. We will leave that as an exercise for you. The XMLCart is primarily a wrapper class around the XmlDocument object. Its primary interfaces will return XmlDocument objects. Let’s get started!

Creating an ADOCatalog

In this section, we will develop the code that allows us to tie our catalog interface to our Web Services. We will store a DataSet in an Application variable to reduce the load on the database, perform copy, clone, import, create, and filter operations on ADO.NET DataSet objects, and use XML and Extensible Stylesheet Language Transformations (XSLT) to render data stored in a DataSet as HTML via the asp:xml server control.

In our ADOCart application, all database interaction is handled via Web Services. Since our “Books” data is fairly static, we can retrieve the data in a DataSet once and store that DataSet in an application-level variable. This reduces the database traffic, while still providing quick access to the data. Here is an overview of the process we will be following:

 Load all Books data to an application variable:

    Application[“AllBooks”];

 Create an instance of ADOCatalog (a.k.a., BookCatalog).

    In Page_onload

 Initialize the instance by passing it.

    (DataSet)Application[“AllBooks”];

 Call BookCatalog.CatalogRange(0,5) to return the first five books.

 Convert return data to XML.

 Load XSLT.

 Set Document and Transform properties of the asp:xml control.

Now, lets create the code. To store our data in an application object, open the Global.asax file. Add this to the Application_onstart method:

localhost.getBooks DataSource = new localhost.getBooks();

Application[“AllBooks”] = DataSource.AllBooks();//DataSet

This will create an instance of the getBooks object called DataSource. Using this instance, we call the AllBooks method, which returns a DataSet. We then save the DataSet in an application-level variable, allbooks.

NOTE

localhost is a reference to the name of the Web Reference containing the getBooks Web Service proxy (getBooks.wsdl).

Now add a new page to the Web Application project (bookSourceUI). Name it start.aspx. Below the #endregion section in the WebForm1 class, we will create a new class called bookCatalog.

Creating the BookCatalog Class

The BookCatalog class will contain the following public methods: InitCatalog, Catalog, CatalogItemDetails, CatalogRange, CatalogByCategory, and the private methods CatalogRangeByCategory, and CreateSummaryTable. The following is a rough prototype of the ADOCatalog class that we’ll be building in this section:

Creating the CreateSummaryTable Method

The CreateSummaryTable method creates a DataTable that contains summary information about the DataSet being returned. This data is used by the XSLT to display Metadata (i.e., viewing records 6 through 12 of 25). It is also useful when making a fetch next range of records call.

Based on the prototype, this method will take the parameters int startPos, int range, and int RecordCount and will return a DataTable. Let’s get started.

1. Create a new empty DataTable named “summary”.

    DataTable dtSummary = new DataTable(“Summary”);

    In the XSD schema this makes the DataTables parent

    element a summary tag (i.e. <summary>)

2. Now add the Columns RecordCount, FirstItemIndex, and LastItemIndex to the Summary DataTable.

3. Create a new DataRow object and assign it to a new DataTable row.

    DataRow drSummary;

    drSummary = dtSummary.NewRow();

4. Populate the DataRow object and add it to the DataTable.

5. Return the new DataTable.

    return dtSummary;

Creating the InitCatalog Method

The InitCatalog method loads a DataSet into the BookCatalog object, then adds a default summary table to the private DataSet dsAllBooks. Based on the prototype, this method will take the only parameter, a DataSet, and will return nothing.

public void initCatalog(DataSet ds)

{

dsAllBooks = ds;

int recordCount = dsAllBooks.Tables[0].Rows.Count;

dsAllBooks.Tables.Add(

createSummaryTable(0, recordCount-1, recordCount));

}

Creating the Catalog Method

The Catalog method returns the entire DataSet stored in the private variable dsAllBooks:

public DataSet catalog()

{

return dsAllBooks;

}

Creating the catalogItemDetails, catalogRange, and catalogByCategory Methods

The three methods, catalogItemDetails, catalogRange, and catalogByCategory, are specialized cases of catalogRangeByCategory and are really only logical interfaces to obtain desired result sets.

The method catalogItemDetails will return all data corresponding with the given ID (Book_isbn):

public DataSet catalogItemDetails(string book_isbn)

{  // returns a DataSet containing a single book

return catalogRangeByCategory(-1, -1, -1, book_isbn);

}

The method catalogRange will return all data for items in a given range:

public DataSet catalogRange(int startPos, int range)

{ //returns a given range of books

return catalogRangeByCategory(startPos, range, -1, null);

}

The method catalogByCategory will return all data for items in a given category:

public DataSet catalogByCategory(int catId)

{ //returns all books with the given categoryId

return catalogRangeByCategory(-1, -1, catId, null);

}

Creating the catalogRangeByCategory Method

The catalogRangeByCategory method creates a new DataSet containing a new Books Table, appends the appropriate Summary Table, and returns this new DataSet. It is used by the preceding methods to return a single item’s node (to add to the shopping cart), to return a range of books (to handle browsing the catalog), and to return all books in a given category (to handle viewing by category). A method could easily be added that enables browsing by category.

In order to return a subset of the DataSet allBooks, we need to create a new DataTable object that has the same table structure as Books. We can then import rows that meet our criteria into this new table. When the table is filled, we create a new DataSet object and add the new DataTable as well as a Summary Table. The resulting DataSet will contain the request subset of data and some Metainformation (supplied by the Summary table).

Now, let’s examine the code. Create a temporary DataTable that holds allBooks data:

DataTable dtTemp = dsAllBooks.Tables[“Books”];

Clone the structure of this table in a new DataTable:

DataTable dtBooks = dtTemp.Clone();//create Empty Books Table

Set the filter expression property based on input parameters:

Set our Data filter to affect all current rows, sort by title, and apply the filter expression:

Add foundRows to the DataTable dtBooks:

for(int i = startPos; i < endPos; i ++)

{

dtBooks.ImportRow((DataRow)foundRows[i]);

}

Add the DataTable dtBooks to the new DataSet along with DataTable Summary, then return this new DataSet:

On page load, we will instantiate the object, retrieve Application[“AllBooks”], return the requested subset DataSet object, convert it to XML using the GetXml() method of the DataSet object, and apply an XSL/Transform to render the Catalog in the UI.

In order to enable browsing, we will store the FirstRecord, LastRecord, recordCount, and user action (previous | next | by CategoryID) into hidden Text fields on the client, so this data can be read to determine which bookCatalog method to call and with which parameters to return the desired subset of AllBooks.

You can see the code on the CD for a closer look at how to implement this class (see start.aspx and start.aspx.cs). The CD also contains the XSLTused to render the UI (Catalog.xslt).

Building an XMLCart

In this section, we will develop the code that allows us to tie our catalog to the shopping cart. We will use XML node operations to update our cart’s contents, XSLT/XPath operations to calculate cart totals and taxes, XML and XSLT to render cart data as HTML, and the asp:XML server control to process transforms. The code for this class can be found on the CD (see start.aspx and start.aspx.cs).

The XMLCart is really a wrapper class around common XML functions. It performs the following basic operations: load data, add new item, remove item, and empty cart.

Looking at the class, you’ll see there really isn’t much to it.

When the page loads, the cart must be initialized. This is handled with the init method. If there is no data to load into the cart, the root node (<shopcartitems>) is added so that child nodes can be imported from the catalog.

When a user chooses to add an item to the shopping cart, the onclick event will call bookCatalog.catalogItemDetails and supply an ISBN. The resulting data will be an XML node for that item. The node will then be imported to the XmlCart document via the method addItem2Cart. The string representation will then be stored in Session[“myShoppingCart”].

When a user selects to remove an item from the shopping cart, the onclick event will remove the node specified by the supplied ISBN via the removeItemFromCart method, and update Session[“myShoppingCart”].

When a user selects Checkout from the shopping cart, the onclick event will call the Web Service orderBooks.OrderItem to update the orders table in the Database, clear the cart via the clearCart method, and display confirmation information to the UI.

When the page is reloaded and the UI needs the latest version of cart, the XML representation is passed via the getCartDescription method:

public string getCartDescriptionString()

{

return myCart.OuterXml;

}

Creating the User Interface

ADOCatalog and XMLCart alone do not provide that much functionality; the real functionality is handled by the showCatalog and the showCart page methods. Before we take a closer look at that, let’s see how the start.aspx page is laid out.

Creating the start.aspx Page

The start.aspx page is the Web form that hosts the controls to generate the UI for our catalog and cart. Here’s the HTML:

<body onload=“initializePagevariables()”>

The preceding code makes a call to a JavaScript function that initializes the values of our hidden field variables.

This next line adds the HTML necessary to draw the navbar. You can also find this file on the CD (see header.htm).

The following asp:dropdown control reads the list of categories from the database and generates a drop-down select box:

The following asp:xml server control transforms the supplied XML data with catalog.xslt (see catalog.xslt on the CD):

<asp:xml id=“catalog” runat=“server”></asp:xml>

</td>

<td valign=“top” align=“middle” bgcolor=“cornsilk”>

The following asp:xml server control transforms the supplied XML data with cart.xslt (see cart.xslt on the CD):

<asp:xml id=“cart” runat=“server”><\asp:xml>

<br>

The following asp:Label server control is used to insert HTML that is dynamically generated when the user clicks checkout:

<asp:Label id=“lblFeedBack” runat=“server”></asp:Label>

</td>

</tr>

</table>

The following div is used to hide a group of text box server controls—so why use a div to hide asp:textbox controlsFirst, while the asp:textbox control does have a visibility attribute, setting this attribute to hidden prevents the HTML from being written to the client, so when we view page source, the HTML for the text box isn’t even there. Second, while using the HTML control <input type=“hidden” runat=“server”> is also an option, this control lacks postback ability.

Each time a user clicks Add, Remove, Checkout, Previous, Next, or makes a change to the drop-down menu for category, we set these hidden variables accordingly and submit the page. Program control is then passed to our code-behind page “start.aspx.cs” (this file can also be found on the CD).

In the following sections, we will see how the user-generated events are handled in our code-behind page: start.aspx.cs.

Rendering the Catalog

On Page_load, we retrieve Application[“AllBooks”] and apply an XSL/Transform to render the Catalog in the UI. In order to enable browsing, we store the FirstRecord, LastRecord, recordCount, and user action (previous | next | by CategoryID) into hidden Text fields on the client, so this data can be read to determine which bookCatalog method to call and with which parameters to return the desired subset of AllBooks.

Rendering the Cart

When a user makes a selection from the catalog (“Add item to cart,” Previous, Next, or selects a category) or the cart (Remove item, or Checkout), the user’s action is stored in hidden text boxes that are passed to the code-behind onsubmit(). In the Page_load method, we will test for addItem, removeItem, or Checkout and handle each accordingly.

Creating the Code

The code for this page can be found on the CD (see:start.aspx.cs). Here is an overview of the page process flow:

 In Page_Load()

1. Get list of categories and bind to asp:dropdownlist control “categories.”

2. Display the default catalog UI by calling showCatalog().

3. Display the default cart UI by calling showCart().

4. Test for Add, Remove, and Checkout. Handle each appropriately.

 In showCatalog()

1. Create an instance of ADOCatalog (a.k.a., bookCatalog).

2. Initialize the instance by loading all book data from Application[“AllBooks”].

3. Test for data filters.

 Did user make a change to the category drop-downFilter “AllBooks” for only the selected category.

 Did user click Previous or NextFilter “AllBooks based on the contents in our hidden textboxes: direction, recordCount, firstRecord, and lastRecord.

 If no filters, use default.

 Set the Document property of the asp:xml control, “catalog” to the filter results.

4. Load XSLT (see catalog.xslt on the CD).

5.  Set Transform properties of the asp:xml control “cart” to catalog.xslt.

 In showCart()

1. Create an instance of XMLCart (a.k.a., xmlShoppingCart).

2. Initialize the instance by loading any previous cart information from Session[“myShoppingCart”].

3. Load XSLT (see cart.xslt on the CD).

4. Set Document and Transform properties of the asp:xml control, “cart” to cart.xslt.

Note that cart and catalog will have already been initialized and rendered before the next three cases can occur.

 In AddItem

1. Retrieve from “AllBooks” the node corresponding to the ISBN value stored in the hidden text box “addItem”.

2. Add this node to our shopping cart.

3. Store updated cart information in Session[“myshoppingCart”].

4. Rewrite the cart to update the UI.

 In RemoveItem

1. Using the ISBN stored in the hidden text box “removeItem,” remove the corresponding XML node from cart.

2. Store updated cart information in Session[“myshoppingCart”].

3. Rewrite the cart to update the UI.

 In Checkout

1. Login user to simulate order processing.

2. Loop through the Nodes in cart and update the orders table, then remove ordered item from cart, while generating the HTML necessary to display the items ordered in an HTML table.

3. Store updated cart information in Session[“myshoppingCart”]; the cart is empty at this point.

4. Rewrite the cart to update the UI.

There are many ways to display data held in a DataSet in XML, or for that matter in ASP.NET. In fact, there are a multitude of controls, including the popular DataGrid control that make this relatively simple. We have opted to use XML and XSLT to show other approaches to the same problem. Also, if your current ASP application uses XML and XSLT, the migration to ASP.NET is fairly easy. In fact, your existing XSLT stylesheets and XML content can still be used. For more information on XSLT, visit www.w3c.org/TR/xslt, www.w3c.org/Style/XSL/#Learn, and www.w3schools.com/XSL.

It is important to note that the Application and Session objects still have issues with regards to server farms and scalability. We used Session in this example for simplicity and to show that it can still be useful. Relatively simple changes can be made to the Start page to convert Session variables into hidden fields stored on the page, or state can be stored in a database.

Summary

We have developed an application that enables customers to browse a catalog of books by category or range, add selections to a virtual shopping cart, remove items from the cart and simulate processing an order by logging in and submitting updates to the order table in the database. We have leveraged the power of XML and its ability to represent data and structure, explored Web Services and their methods, designed databases and stored procedures, developed custom code-behind classes in C# and covered a multitude of uses for ADO.NET.

We also explored database design and implementation, creating two databases for the application, one for Access and one for SQL. We then covered entities and their attributes and how both work with each other to create a normalized database. Lastly, we developed a set of stored procedures that will handle all data interaction with the database, preventing the use of “ad hoc” queries against the database. To see the ADOCart application on the Web (it is available on the CD accompanying this book), visit www.DotThatCom.com.

Solutions Fast Track

Setting Up the Database

 A relationship between the two tables is created by the use of primary and foreign keys.

 The different types of relationships between tables are one-to-one, one-to-many, and many-to-many. In a one-to-one relationship, exactly one row corresponds with a matching row of the related table. In a one-to-many relationship, one row corresponds to many rows of the related table. In a many-to-many relationship, many rows correspond to many rows of the related table.

 Using parameterized queries in MS Access and stored providers in MS SQL results in performance gain. In addition, you no longer have to run ad hoc queries against the database. Pre-complied queries perform better.

Creating the Web Services

 Web Services provide separation of the data tier from the user interface (UI). This also makes it possible to access our data from any platform.

 Web Services help separate our data tier from our application logic. This creates a more robust and portable application.

 Web Services leverage the power of XML and its interoperability. All pages can communicate with the common language and exist in the same context.

Using WSDL Web References

 Disco, or vsdisco, written in WSDL, enables access to all Web Services and methods for that site. This provides a one-stop shop, if you will, into the server’s cupboards.

 Proxy classes can easily be generated using WSDL, which enables code to access remote services as if they were local classes.

Building the Site

 Create an overview of the site structure: what pieces need to be built and how the pages relate to one another. In our example, we focus on the middle tier data classes and controls that act as a bridge between the backend and the Web UI.

Site Administration

 Tie the site administration to the Web Services, enabling the administration functions for the site to be done without accessing the code or database. The adminPage.aspx page in our example allows the site administrator to retrieve and display data, and to add, delete, and update product.

 To retrieve the list of books stored in the database, we need to access the “GetAllBooks” stored procedure (MSSQL) or parameterized query (MS Access) by creating the allBooks method of the getBooks Web Service. This method will take no parameters, and will return a DataSet containing all Book data as well as the table structure of the Database table that the data originated from.

 The DataSet is an in-memory XML representation of the database, including the Books table.

 Display the data by binding a DataGrid to the DataSet. The DataGrid is actually bound to the DataTable Books which is a table within the DataSet returned by getBooks.AllBooks. We create a DataView of the Books table so we can sort the data. This DataView is then bound to the DataGrid.

 Using the DataGrid event changeBooks_DeleteCommand, fired when a user clicks the Delete button in the DataGrid UI, we can select a row in the DataGrid to delete by using the RowFilter property.

 Using the DataGrid event changeBooks_UpdateCommand, fired when a user clicks the Update button in the DataGrid UI, we can select the row in the DataGrid to update by using the RowFilter property.

Customer Administration

 The Customer Administration pages tie our customer administration interface to our Web Services, enabling the customer to update their personal information. This is an added benefit to the user of the site.

 Customer administration will be identical to the example of the site administrator, except we will enable the customer to update only.

Creating an ADOCatalog

 Creating an ADOCart application allows us to tie our catalog interface to our Web Services. In our ADOCart application, all database interaction is handled via Web Services.

 Create a new class to explore ADO.NET DataSet operations in order to: copy, clone, import, create, and filter.

 Since our “Books” data is fairly static, we can retrieve the data in a DataSet once and store that DataSet in an application-level variable. This reduces the database traffic, while still providing quick access to the data.

 Use XML and XSLT to render data stored in a DataSet as HTML via the asp:Xml server control.

Building an XMLCart

 Building an XMLCart allows us to tie our catalog to the shopping cart.

 We will use XML node operations to update our cart’s contents, XSLT/XPath operations to calculate cart totals and taxes, XML and XSLT to render cart data as HTML, and the asp:XML server control to process transforms.

 An XmlDocument wrapper class provides add, remove, clear, and checkout operations.

Creating the User Interface

 ADOCatalog and XMLCart alone do not provide that much functionality; the real functionality is handled by the showCatalog and the showCart page methods.

    The start.aspx page is the Web Form that hosts the controls to generate the UI for our catalog and cart.

 Use of XML and XSLT generates portions of the UI via the asp:xml server controls.

Frequently Asked Questions

The following Frequently Asked Questions, answered by the authors of this book, are designed to both measure your understanding of the concepts presented in this chapter and to assist you with real-life implementation of these concepts. To have your questions about this chapter answered by the author, browse to www.syngress.com/solutions and click on the “Ask the Author” form.

Q: My project has a few different pages in it. Unfortunately, the last page I created is the one that is loaded when I run the project. How do I set the first page to open when I run the project?

A: In your Project Explorer, right-click the file you want and set it to Start Page.

Q: I am working with the XmlDocument object in my code-behind page, and I am not getting any IntelliSense. What am I doing wrong?

A: Make sure you have included “Using System.Xml” in the top section of the page.

Q: I just started using VS.NET Beta 2 and I am trying to create a WSDL proxy to my Web Service. Is there an easy way to do this in VS.NET?

A: Right-click your Project Explorer and select Add Web reference.

Q: I renamed a file in my Solutions Explorer, but the corresponding “.aspx.cs” and “.aspx.resx” names did not change. Because of this, the project will not compile correctly. How can I fix this?

A: In your Solutions Explorer, make sure all child files are collapsed in the parent when renaming and this will change all the associated files. If you have already changed one file, change it back to the name prefix of the other files, then collapse the children and rename it to the new name. Also, check the first line in the .aspx page and ensure that the Inherits attribute lists the correct filename.