Chapter 34. Building an E-Commerce Application

<feature><title>In this Chapter</title> <objective>

Overview of the E-Commerce Application

</objective>
<objective>

Using Master Pages, Themes, and User Controls

</objective>
<objective>

Building a Component Library

</objective>
<objective>

Creating a Custom Site Map Provider

</objective>
<objective>

Creating a Shopping Cart

</objective>
<objective>

Protecting Credit Card Numbers

</objective>
<objective>

Handling Images

</objective>
<objective>

Retrieving Data with AJAX

</objective>
<objective>

Improving Performance Through Caching

</objective>
<objective>

Conforming to Standards

</objective>
<objective>

Summary

</objective>
</feature>

The problem with most code samples in books is that they are way too simple. When building a full application, the world is often much more complex than the simple world that the book describes.

In this chapter, we build a complete ASP.NET application from start to finish. We build an entire online store with the ASP.NET Framework.

This chapter has several goals. The first is to discuss the issues that I encountered while building the application. Hard decisions had to be faced. Trade-offs were made.

Second, this book covers a lot of material. The ASP.NET 2.0 Framework includes an overwhelming number of new features (a fact which is both cool and scary). This chapter draws together many of the new ASP.NET 2.0 technologies discussed separately in previous chapters and shows you how you can apply these technologies in the context of a real-world application.

Finally, an important goal of this chapter is to provide you with a functioning application that you can use as a starting point for your projects. All the code for the e-commerce application is included on the CD that accompanies this book in both a Visual Basic .NET and C# version. If you need to build an e-commerce application, you can take advantage of this code to save yourself a significant amount of time.

Overview of the E-Commerce Application

In this chapter, we build the ASP.NET Beer Store. The e-commerce application is loaded with sample data that represents beer product information (see Figure 34.1).

Home page of the ASP.NET Beer Store.

Figure 34.1. Home page of the ASP.NET Beer Store.

Note

The sample images used for the ASP.NET Beer Store were generously provided by Nathan Wiger and Corey Gray, the world-renowned experts on all excellent forms of beer. Visit their website at www.BeerLabels.com.

Before we get into the technical details of how the application works, I want to provide you with an overview of the different parts of the application. This application has two halves: The half that the world sees and the half that the store administrators see.

The public half of the ASP.NET Beer Store consists of the following ASP.NET pages:

  • Default.aspx—. The home page of the ASP.NET Beer Store. This page displays a list of featured products.

  • Products.aspx—. This page displays a list of products contained in a particular product category. If the category contains sub-categories, the sub-categories are also displayed.

  • ProductDetails.aspx—. This page displays details for a particular product. It also contains a link for adding a product to a shopping cart.

  • ShoppingCart.aspx—. This page displays a customer shopping cart. Customers can use this page to edit the items in their shopping carts.

  • CheckOutDefault.aspx—. This page enables customers to enter billing and shipping information and purchase the products in their shopping carts.

  • CheckOutConfirmation.aspx—. This page displays an order confirmation number after a customer has placed an order.

  • ContactInfo.aspx—. This page displays contact information for the store.

  • Login.aspx—. This page enables an existing customer to log in or a new customer to register.

  • PasswordReminder.aspx—. This page enables customers to reset their passwords when they have forgotten their original passwords.

When you request the home page of the application, a list of featured products is displayed. Every page in the application also displays a list of product categories in a menu in the left column.

If you navigate to a product category, then you arrive at the Products.aspx page. This page displays a list of products contained in the category in a DataList control (see Figure 34.2). Next to each product description, a View Details link is rendered. If you click the View Details link, then you arrive at the ProductDetails.aspx page.

The Products.aspx page.

Figure 34.2. The Products.aspx page.

The ProductDetails.aspx page displays information on a particular product. You can click the Add to Cart link to add the product to your shopping cart.

You can view the contents of your shopping cart by clicking the Shopping Cart menu link that appears at the top of any page. The shopping cart is displayed by the ShoppingCart.aspx page. This page enables you to remove items from the shopping cart. It also includes a Check Out link (see Figure 34.3).

The ShoppingCart.aspx page.

Figure 34.3. The ShoppingCart.aspx page.

If you click the Check Out link, and you are not authenticated, then you arrive at the Login.aspx page. This page enables existing customers to log in and new users to register. After you log in or register, you are redirected to the CheckOutDefault.aspx page (see Figure 34.4).

The check out page.

Figure 34.4. The check out page.

The CheckOutDefault.aspx page contains a form that enables a customer to enter credit card information, billing address, and shipping address. When the customer submits the form, the shopping cart associated with the customer is converted into a new product order.

The private section of the application is no less important. A store manager uses this section to list new products and view customer orders. The private half of the ASP.NET Beer Store consists of the following pages:

  • ManageDefault.aspx—. Contains a list of links to other pages in the store management section.

  • ManageCategoriesDefault.aspx—. This page enables store managers to create new product categories and edit existing product categories.

  • ManageProductsDefault.aspx—. This page enables store managers to list new products and edit existing products.

  • ManageOrdersDefault.aspx—. This page enables store managers to view customer product orders.

When the ASP.NET Beer Store first starts, an administrator role and user is created automatically. The role is named StoreAdmins and the user is named Admin. The Admin user has the password secret.

If you login with the Admin account, then an additional menu item labeled Manage appears at the top of every page. If you click the Manage link, then you are brought to the ManageDefault.aspx page. This page displays a menu of management options.

Warning

Make sure that you modify the Admin password. Everyone who reads this book knows that the Admin password defaults to secret. You can modify the Admin password by opening the Web Site Administration Tool when the application is loaded in Visual Web Developer. Launch this tool by selecting the menu option Website, ASP.NET Configuration.

If you navigate to the ManageCategoriesDefault.aspx page, then you can add, delete, and edit product categories. The hierarchy of current product categories is displayed in a TreeView control. Child categories of the selected category in the TreeView are displayed in a GridView control. If you click the Add Category link, a floating virtual window appears that enables you to add a new product category (see Figure 34.5).

Adding a product category.

Figure 34.5. Adding a product category.

The ManageProductsDefault.aspx page enables you to add, delete, and edit products. The list of current products is displayed in a GridView control. If you click the Add New Product link, a new floating virtual window appears that contains a form for entering a new product (see Figure 34.6).

Adding a new product.

Figure 34.6. Adding a new product.

The ManageOrdersDefault.aspx page displays a list of product orders. This page doesn’t display any product orders until a customer submits a shopping cart. You can click the Select link next to any order to view detailed order information, including the customer credit card information, billing address, and shipping address (see Figure 34.7).

Viewing customer orders.

Figure 34.7. Viewing customer orders.

Using Master Pages, Themes, and User Controls

The ASP.NET Beer Store is designed with Master Pages, Themes, and User Controls. Master Pages and User Controls are used to share content across multiple pages. Themes are used to give the pages in the application a common style.

Note

Master Pages, Themes, and User Controls are covered in Part II of this book, “Designing ASP.NET Websites.”

The application uses two Master Pages named Store.master and Manage.master. The Store.master Master Page contains the layout for the public section of the website. The Manage.master page contains the layout for the private section of the website.

The Store.master Master Page contains two of the standard ASP.NET Navigation controls: the Menu control and the SiteMapPath control. The Menu control is used to display the list of product categories. The SiteMapPath control is used to display the breadcrumb that appears near the top of every page.

The application takes advantage of two User Controls to create a standard layout for displaying products and product categories. The TemplatesProductTemplate.ascx User Control is used in both the Featured Products DataList and the Products DataList to display product information. The TemplatesCategoryTemplate.ascx User Control is used in the Categories DataList to display product category information.

Building a Component Library

All the application logic for the e-commerce application was pushed out of the pages and into a separate component library. Placing as much of your application logic as possible into separate components makes it easier to reuse the same methods across multiple pages in your website.

The components are contained in the App_Code folder. The contents of this folder are dynamically compiled. You don’t need to perform an explicit Build before using the components in the App_Code folder in your pages.

Note

Components are discussed in Part IV of this book, “Building Components.”

The App_Code folder contains the following components:

  • Category—. Represents a product category. Contains methods for adding, deleting, and editing product categories.

  • Product—. Represents a product. Contains methods for adding, deleting, and editing products.

  • ShoppingCart—. Represents a shopping cart. Contains methods for adding and deleting shopping cart items.

  • Order—. Represents a product order. Contains methods for converting a shopping cart into a product order and retrieving orders.

Each component performs a dual role. Each component represents a particular type of object and it also represents methods for working with objects of that type. For example, the Product component represents a product. It includes all of the product properties such as the product Name and Price properties.

However, the Product component also contains all the methods for interacting with products. For example, it includes a method named SelectByCategoryId() that retrieves all the products contained in a particular category.

Creating a Custom Site Map Provider

One of the first issues that I encountered when building the e-commerce application was the problem of representing product categories.

On the one hand, I wanted to use all the standard Navigation controls. I wanted to use the Menu, SiteMapPath, and TreeView controls to display the product categories. For example, as you move from page to page in the website, I wanted the SiteMapPath control to display the current product category automatically so that a customer could navigate back up the category hierarchy. Because I wanted to use the product categories with the standard navigation controls, I needed to represent the categories in a Site Map.

Note

Navigation controls and Site Maps are discussed in Part V of this book, “Site Navigation.”

On the other hand, I wanted to enable administrators of the application to be able to modify product categories easily through a form interface. I wanted an administrator to be able to add and edit categories through the form interface contained in the ManageCategoriesDefault.aspx page.

Unfortunately, the default Site Map provider doesn’t support both requirements. The default Site Map provider uses XML files to represents the navigational structure of a website. An administrator cannot easily update an XML file through a form interface. Therefore, I decided to write a custom Site Map provider.

Product categories are represented with the CategorySiteMapProvider. This class is contained in the App_Code folder. The CategorySiteMapProvider stores navigation information in a SQL database table. The CategorySiteMapProvider is configured to retrieve the categories from a database table named Categories.

The ASP.NET Beer Store actually uses both the standard XmlSiteMapProvider and the CategorySiteMapProvider to represent a Site Map. The root Web.sitemap file is contained in Listing 34.1.

Example 34.1. Web.sitemap

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
  <siteMapNode url="~/default.aspx" title="Home"  description="Home page">
    <siteMapNode provider="CategorySiteMapProvider" />
    <siteMapNode url="~/shoppingcart.aspx" title="Shopping Cart"
          description="View Shopping Cart" />
    <siteMapNode url="~/contactinfo.aspx" title="Contact Us"
          description="Contact us by phone or email" />
    <siteMapNode siteMapFile="~/manage/web.sitemap" />
  </siteMapNode>
</siteMap>

Notice that the second siteMapNode element in Listing 34.1 specifies a particular provider. This node retrieves all its subnodes from the CategorySiteMapProvider.

Notice, furthermore, that the last siteMapNode element uses the siteMapFile attribute. The Site Map for the management section of the website is contained in a separate XML Site Map file located at ManageWeb.sitemap.

The ASP.NET Framework is smart enough to merge the SiteMapNodes from these different providers and locations seamlessly in the background. I was surprised with how little work I had to perform to combine the SiteMapNodes from the XML Site Map files and the SQL Server database table.

Creating a Shopping Cart

I also struggled with the issue of how to represent customer shopping carts. The obvious choice here is the ASP.NET Profile object. Using the Profile object offers several advantages.

Note

Browser cookies, Session state, and the Profile object are discussed in Chapter 22, “Maintaining Application State.”

First, the Profile object is persistent. A customer can add items to a shopping cart during one visit and return many months later to complete a purchase.

Second, the Profile object is designed to handle both anonymous and authenticated users. Requiring a customer to register before adding items to a shopping cart does not create a good customer experience. An e-commerce application should make the process of adding an item to a shopping cart as easy as possible.

Finally, when you take advantage of the Profile object, you don’t need to write any database logic to store shopping cart information. The Framework does all the hard work for you.

This all sounds good. Unfortunately, I encountered one issue with the Profile object that I could not overcome. I wanted to be able to perform database joins between the items in a shopping cart and other database tables such as the Products database table.

For example, when a customer views his shopping cart, the shopping cart should not display the price of a product when the customer added the item to their shopping cart. Instead, the shopping cart should display the current price of the product (imagine that a customer adds an item to the shopping cart while the item is on sale and returns many months later).

When you store items with the Profile object, all the items are stored as a blob. You can’t perform database queries against the individual items contained in a Profile. In particular, you can perform database joins between items in a Profile and other database tables.

Therefore, I created a custom ShoppingCart component to represent customer shopping carts. The ShoppingCart component is in the App_Code folder.

The custom ShoppingCart component persists customer shopping carts. The component stores shopping carts in a database table named ShoppingCarts.

The custom ShoppingCart component handles both anonymous and authenticated users. It does it the same way that the Profile object does. Anonymous Identification is enabled in the web configuration file. When Anonymous Identification is enabled, a persistent cookie containing a unique identifier is added to each anonymous customer’s browser. This unique identifier is used when an anonymous customer’s shopping cart is stored and retrieved.

The Global.asax file includes a Profile_OnMigrateAnonymous() event handler. This event handler calls a method of the ShoppingCart class named AuthenticateCart() when an anonymous user logs in or registers. This method updates the ShoppingCarts database table by replacing the customer’s anonymous identifier with the customer’s authenticated username.

Finally, the custom ShoppingCart component caches customer shopping carts in Sessions state. A customer’s shopping cart does not need to be retrieved from the database with each page request. Instead, the shopping cart is stored in the web server’s memory while the customer browses the website. This is done to improve performance.

Protecting Credit Card Numbers

Storing credit card numbers in plain text in the database is an extremely bad idea. If a customer trusts you with a credit card number, you should do everything in your power to protect the information.

The best option is to never store credit card numbers at all. If you process a customer credit card number immediately after the customer submits it, then you can discard the credit card number when the transaction completes.

Note

If you want to modify the e-commerce application to process credit cards immediately, one easy way to do this is to take advantage of the PayPal SDK. To learn more about the PayPal SDK, visit the following website:

http://www.paypal.com/cgi-bin/webscr?cmd=xpt/cps/general/SoftwareDevKit-outside

The e-commerce application stores credit card numbers in the Orders database table. Credit card numbers are not stored in plain text. Instead, they are encrypted before being added to the database.

The e-commerce application uses a component named Secret to encrypt and decrypt credit card numbers. The Secret component is located in the App_Code folder.

Note

You should use a Secure Sockets Layer (SSL) connection between a browser and web server whenever a user submits sensitive information, such as a credit card number, in a form. SSL encrypts the data that is passed across the Internet. You can enable SSL when serving pages with Internet Information Server by installing an SSL certificate. You need to purchase an SSL certificate from a Certificate Authority such as Verisign (www.verisign.com) or Thawte (www.thawte.com).

The Secret component uses the RijndaelManaged class from the System.Security.Cryptography namespace to encrypt and decrypt strings. The Rijndael algorithm is also known as the Advanced Encryption Standard (AES). It is the United States government encryption standard.

To use the RijndaelManaged class to encrypt a string, you must supply an encryption key and an initialization vector (IV). The encryption key must be kept secret. The IV, on the other hand, does not need to be kept secret. You need both the encryption key and IV to decrypt an encrypted string.

The Secret component loads the encryption key from the machineKey section of the web configuration file. The component reads the value of the decryptionKey attribute. The component uses the same key that is used by the ASP.NET Membership framework. The IV is generated from the first bytes of the encryption key.

If you change the value of the decryptionKey attribute in the web configuration file, then you can’t retrieve any of the credit card numbers stored in the database. Credit card numbers are retrieved as a string of question marks.

Warning

The sample application contains a machineKey section with a decryptionKey attribute in the web configuration file. You need to change the value of the decryptionKey attribute to a new value. You can generate a new decryptionKey by using the GenerateKeys.aspx page described in Chapter 21, “Using ASP.NET Membership.”

Of course, all this encryption is meaningless if a hacker gets access to the ManageOrdersDefault.aspx page. This page displays order information, including the credit card number associated with an order. The page is password protected so that only members of the StoreAdmins role can access the page. However, if a hacker manages to bypass the ASP.NET Authentication framework, then all bets are off.

Handling Images

When you add a product with the ManageProductsDefault.aspx page, you can add an image for the product. The product image is stored in the Products database table.

Note

The FileUpload control in is covered in Chapter 4, “Using the Rich Controls.”

The ManageProductsDefault.aspx page uses the standard ASP.NET FileUpload control to upload the image. The image is read from the FileUpload control and inserted into the database with the Product.InsertImage() method. To avoid clobbering the entire memory of your web server with a large image, this method adds the image to the database incrementally in 8040-byte chunks.

The product images are displayed by the Default.aspx, Products.aspx, and ProductDetails.aspx pages. All these pages use a Generic Handler named ProductImage.aspx to retrieve the image. This handler retrieves the image in 8040-byte chunks from the database and sends the image bytes to the browser.

Note

Generic Handlers are discussed in Chapter 25, “Working with the HTTP Runtime.”

Retrieving Data with AJAX

No ASP.NET 2.0 application should be written without at least a little bit of AJAX (and a lot is great). The e-commerce application includes an AjaxRotator control that displays a random content item retrieved from the web server. The AjaxRotator updates its contents on the browser automatically every 15 seconds.

The AjaxRotator is included in the Store.master Master Page, so it is included on every public page in the website (see Figure 34.8). In the case of the ASP.NET Beer Store, the AjaxRotator is used to randomly display different beer facts (of questionable veracity).

The AjaxRotator control displays beer facts.

Figure 34.8. The AjaxRotator control displays beer facts.

The AjaxRotator retrieves its content items from an XML file named AjaxRotatorContent.config. This XML file contains a list of <item> elements, each of which represents a content item. The <item> elements can contain HTML content just as long as the content is XHTML-compliant.

Note

AJAX is covered in Chapter 7, “Creating Custom Controls with User Controls,” and Chapter 32, “Integrating JavaScript in Custom Controls.”

Improving Performance Through Caching

The best way to improve the performance of an ASP.NET application is through caching. The e-commerce application takes extensive advantage of the new caching features of the ASP.NET 2.0 Framework.

The list of products, list of featured products, and list of categories are displayed with three user controls named ProductView.ascx, FeaturedProductView.ascx, and CategoryView.ascx. All three of these user controls include an <%@ OutputCache %> directive that includes a SqlDependency attribute.

All three user controls use a Polling SQL Cache dependency. The user controls cache data in memory until the data changes in the underlying database. For example, the list of featured products displayed on the home page (Default.aspx) is cached by the FeaturedProductView.ascx user control. The rendered output of this user control is cached in memory until the contents of the Products database table is changed.

The product information displayed by the ProductDetails.aspx page is also cached. However, in this case, the caching is performed at the level of the DataSource control rather than at the level of a User Control. The product information is retrieved from an ObjectDataSource control. The ObjectDataSource control is configured to use a Polling SQL Cache Dependency.

Warning

The e-commerce application is configured to poll the database for changes every 15 seconds so data can be up to 15 seconds out of date. You can configure a shorter interval by modifying the pollTime attribute of the <sqlCacheDependency> element in the web configuration file.

Finally, the customer shopping carts are cached in Session state. When a shopping cart is first retrieved from the database for a customer, the shopping cart is added to Session state and remains there until the shopping cart is modified or the customer leaves the website.

Conforming to Standards

The e-commerce application is standards friendly. It was written to conform to the XHTML 1.0 Transitional standard. You’ll notice that the footer includes an icon from the World Wide Web Consortium (W3C) that indicates that the website has successfully passed their XHTML 1.0 Transitional validator (see Figure 34.9).

XHTML 1.0 Transitional W3C icon.

Figure 34.9. XHTML 1.0 Transitional W3C icon.

You can validate any page against the W3C validator by visiting http://validator.w3.org.

The e-commerce application does not use HTML tables for layout. All page layout is performed with Cascading Style Sheets. For example, the Store.master Master Page contains three <div> tags that represent the three page columns. The three columns are laid out from left to right with the following three CSS classes:

.leftColumn
{
    float:left;
    width:100px;
    padding:5px;
}

.middleColumn

{
    float:left;
    width:450px;
    border-left:solid 1px blue;
    padding:8px;
}

.rightColumn
{
    float:right;
    width:200px;
    border-left:solid 1px blue;
    border-bottom:solid 1px blue;
    padding:5px;
}

Furthermore, the e-commerce application was designed to be accessible to persons with disabilities. All images include an ALT attribute. All form elements are explicitly associated with a label. For example, the input field for a product name is created with the following Label and TextBox controls:

<asp:Label
  id="lblName"
  Text="Name:"
  AssociatedControlID="txtName"
  Runat="server" />

<asp:TextBox
  id="txtName"
  Text='<%# Bind("Name") %>'
  Runat="server" />

The AssociatedControlID property is used to explicitly associate the Label control with the TextBox control.

Web Standards Note

To learn more about how to make accessible ASP.NET 2.0 websites, see my article at the Microsoft MSDN website, “Building ASP.NET 2.0 Web Sites Using Web Standards.”

Summary

The e-commerce application illustrates many of the new features of the ASP.NET 2.0 Framework. First, it illustrates how you can take advantage of Master Pages and Themes when designing your website. The e-commerce application takes advantage of both technologies to make it easy for you to change the appearance of the website.

The e-commerce application also takes advantage of the new Navigation controls and Site Map infrastructure included in the ASP.NET 2.0 Framework. The application uses TreeView, Menu, and SiteMapPath controls. These controls are bound to Site Map data retrieved from either the standard XmlSiteMapProvider or the custom CategorySiteMapProvider.

The e-commerce application also takes advantage of the new performance-enhancing features of the ASP.NET 2.0 Framework. The application uses Polling SQL Cache Dependencies to cache product and category data in memory just as long as the data does not change in the database.

Finally, the e-commerce application conforms to W3C standards such as XHTML and accessibility standards. The entire website validates as XHTML 1.0 Transitional.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset