ASP.NET 2.0 Instant Results
.pdfWrox WebShop
Figure 9-4
Design of the WebShop
To make the WebShop easy to maintain, it’s based on a three-tier architecture where the ASPX pages are separated from the business layer and data access code. The code for the business layer is located in the BusinessLogic folder in the special App_Code folder, and the data access layer can be found in the DataAccess folder. The presentation layer, consisting of .aspx and .ascx files, is located in the root of the site and in a few subfolders that are listed later.
The Business Layer
The business layer consists of five classes that are stored in the BusinessLogic folder inside the App_Code folder in the root of the web site.
Because each file in the business layer contains only one class, the file is named after the class. So you’ll find the Product class in the file Product.vb, and so on.
Product
The Product class (see Figure 9-5) represents the products that are displayed on the web site; it does not represent the actual ordered product customers can add to their shopping cart, although the two are closely related.
277
Chapter 9
Figure 9-5
The Product class itself has no defined behavior. That is, it has only properties and no methods other than a default constructor. All interaction with products, such as getting a list of products or a product instance, is carried out by the ShopManager class, which is discussed later. In addition to Id, Price, and Description properties, the Product class also has multiple PictureURL properties that are used to display images of the product in the product catalog, the detail page, and the shopping cart. The following table lists each of the eight properties of the Product class and explains their usage:
Property |
Type |
Description |
|
|
|
CategoryId |
Integer |
The database of the WebShop has a Category table to |
|
|
identify the various product categories. Each product is |
|
|
then linked to that table through its CategoryId. |
Description |
String |
This is the full description of the product, allowing you to |
|
|
provide detailed information about it. |
Id |
Integer |
This is the unique ID of the product in the database. The |
|
|
ID is assigned by the database automatically whenever a |
|
|
new product is inserted. |
PictureUrlLarge |
String |
This property contains a virtual path to the large image of |
|
|
the product. This image is used on the detail page for |
|
|
each product. |
PictureUrlMedium |
String |
This property contains a virtual path to a medium-sized |
|
|
thumbnail image of the product. This image is used on |
|
|
the product catalog with a list of products. |
PictureUrlSmall |
String |
This property contains a virtual path to a small thumbnail |
|
|
image of the product. This small image is used in the |
|
|
shopping cart. |
Price |
Decimal |
The price of the product. |
Title |
String |
This is the title of the product used to identify the prod- |
|
|
uct in the catalog and in the shopping cart. |
|
|
|
278
Wrox WebShop
You should note that a product does not have properties like Quantity to indicate the number of items a user wants to order of a specific product. Whenever a user adds a product to the shopping cart, that product is wrapped inside an instance of the OrderedProduct class that does have these properties. The OrderedProduct class is discussed next.
OrderedProduct
In Figure 9-6, a diagram of the OrderedProduct class, you see a lot of properties that the Product class has as well. That’s no surprise, because an OrderedProduct has a lot in common with a Product. To avoid duplication of functionality and copying information from a Product to an OrderedProduct whenever an item is added to the shopping cart, the OrderedProduct class has a private member of type Product. When a new instance of an OrderedProduct is created, an instance of the Product class is passed to its constructor, which is then stored in _theProduct. Properties such as Description and PictureUrlSmall then forward their calls to the inner Product to get at the actual values.
Figure 9-6
Besides the properties that delegate their responsibility to the inner Product object, the OrderedProduct class has the following additional properties:
Property |
Type |
Description |
|
|
|
Id |
Guid |
A unique ID to identify each product in the shopping cart. |
|
|
This ID is generated whenever a new instance of Ordered |
|
|
Product is created, and is used when existing items are |
|
|
updated or removed from the cart. |
ProductId |
Integer |
The ID of the underlying product. |
Quantity |
Integer |
The number of items of the product that a user has ordered. |
SubTotal |
Decimal |
Returns the read-only subtotal for the OrderedProduct |
|
|
items by multiplying their quantity and the price of the |
|
|
inner Product. This property is used in the shopping cart |
|
|
to display the subtotal for each item. |
|
|
|
279
Chapter 9
So far you have looked at classes that contain information, but cannot perform any actions. To do something useful with these classes you need some action classes that can operate on Products and OrderedProducts. Those classes are the ShoppingCart and the ShopManager, which are discussed next. Just as the Product and OrderedProduct classes, you’ll find these two classes in the BusinessLogic folder in the App_Code folder.
ShoppingCart
The ShoppingCart class (see Figure 9-7) is, as its name implies, the central storage location for OrderedProducts. An instance of the ShoppingCart class is stored in a simple session variable and made accessible through a shared property on the ShopManager class. This way, all the pages and other classes in the site can access the cart.
Figure 9-7
The ShoppingCart class contains a list with ordered products and a few methods to add, update, and remove those items. It also has properties to access the items in the shopping cart, get an item count, and get a total order amount for all the ordered products. Finally, it has a default, parameterless constructor to create new instances of type ShoppingCart.
The following table lists the properties of the ShoppingCart class:
Property |
Type |
Description |
|
|
|
Count |
Integer |
Returns the total number of ordered items. It does |
|
|
this by looping through the _items collection, |
|
|
asking each OrderedProduct for its Quantity. |
|
|
This property is read-only. |
Items |
List (Of OrderedProduct) |
Provides read-only access to the _items list. To |
|
|
add, update, or insert items in the list, the public |
|
|
methods of the ShoppingCart class must be used. |
|
|
This property is read-only. |
Total |
Decimal |
Returns the total amount of money for the entire |
|
|
order. It does this by looping through the _items |
|
|
collection, asking each OrderedProduct for its |
|
|
SubTotal. This property is read-only. |
|
|
|
280
Wrox WebShop
To work with the items in the shopping cart the class exposes the following methods:
Method Name |
Return |
Description |
|
Type |
|
|
|
|
Public Sub Add |
n/a |
Adds a new OrderedProduct to the shopping |
(ByVal theProduct |
|
cart. When the item is already present, its quantity |
As Product) |
|
is increased instead of adding a new instance to the |
|
|
cart. The product passed to this method is wrapped |
|
|
inside an OrderedProduct instance that in turn is |
|
|
added to the cart. |
Public Sub Clear () |
n/a |
Removes all items from the cart. |
Public Sub Remove |
n/a |
Removes an item from the cart based on its unique ID. |
(ByVal id As Guid) |
|
|
Public Sub Update |
n/a |
Updates the quantity for an existing item in the cart |
(ByVal newQuantity |
|
based on its unique ID. |
As Integer, ByVal id |
|
|
As Guid) |
|
|
|
|
|
These four methods are never accessed by the ASPX pages in the presentation layer directly. The presentation layer should call one of the public methods on the ShopManager class that in turn call these methods on the ShoppingCart class.
ShopManager
The ShopManager class (see Figure 9-8) is the central entity in the application that deals with Products and OrderedProducts. It is used in two parts of the application: in the front end to provide access to the shopping cart and in the back end to allow an administrator to manage the products in the product catalog.
Figure 9-8
281
Chapter 9
In addition to the ShoppingCart property, which is of type ShoppingCart, discussed earlier, the ShopManager has the following public methods:
Method Name |
Return |
Description |
|
Type |
|
|
|
|
Public Shared Sub |
n/a |
Adds a new OrderedProduct to the shopping |
AddProductToCart |
|
cart by calling the Add method of the Shopping |
(ByVal theProduct |
|
Cart class. |
As Product) |
|
|
Public Shared Sub |
n/a |
Deletes a product from the product catalog. This |
DeleteProduct |
|
method is used in the maintenance section of the |
(ByVal theProduct |
|
WebShop. |
As Product) |
|
|
Public Shared |
Integer |
Finalizes an order for a customer. This method |
Function FinalizeOrder |
|
calls the FinalizeOrder method in the data |
(ByVal theCustomer |
|
access layer to insert the order in the database |
As Customer) |
|
and then returns the new order ID. |
Public Shared |
Product |
Returns a single instance of a product. This |
Function GetProduct |
|
method is used in the Product Details page to |
(ByVal theProductId |
|
display information about a specific product. |
As Integer) |
|
|
Public Shared |
DataSet |
Returns a DataSet with the available product |
Function GetProduct |
|
categories used in the catalog and the maintenance |
Categories() |
|
section. |
Public Shared |
List |
Returns a list with products in the specified |
Function GetProductList |
(Of Product) |
category. Used to display products in the product |
(ByVal theCategoryId |
|
catalog. |
As Integer) |
|
|
Public Shared Function |
List |
Returns a list with OrderedProducts from the |
GetShoppingCartItems () |
(Of Ordered |
ShoppingCart by accessing its Items property. |
|
Product) |
|
Public Shared Sub |
n/a |
Inserts a new product in the product catalog. This |
InsertProduct (ByVal |
|
method is used in the maintenance section of the |
theProduct As Product) |
|
WebShop. |
Public Shared Sub |
n/a |
Removes an OrderedProduct from the shopping |
RemoveProductFromCart |
|
cart by calling the Remove method of the Shopping |
(ByVal id As Guid) |
|
Cart class. |
Public Shared Sub |
n/a |
Updates an existing OrderedProduct in the |
UpdateProductInCart |
|
shopping cart by calling the Update method of |
(ByVal newQuantity |
|
the ShoppingCart class. |
As Integer, ByVal id |
|
|
As Guid) |
|
|
|
|
|
282
Wrox WebShop
Besides these methods, the ShopManager class has a single, hidden constructor. By hiding the constructor (using the access modifier Private) you can prevent calling code from creating useless instances of the ShopManager class. Because the class exposes only shared methods and properties, there is never the need for an instance of the ShopManager.
Obviously, a shop needs customers to stay in business and the Wrox WebShop is no exception. The next section shows you what the Customer class in the WebShop looks like.
Customer
The final class in the business layer you should look at is the Customer class. The WebShop uses the Membership provider and the Profile classes to store information about the user such as login name, password, and address details. Because all of this is handled by ASP.NET automatically, why do you need an additional Customer class?
The Customer class is used to reflect the user’s details at the moment of a purchase in the WebShop. From a security standpoint it’s a good practice to disallow users to change their details, such as the shipping address, after the purchase has been finalized. This way, for example, hackers cannot change the shipping address after the order has been paid and redirect the goods to their address instead of to the customer’s.
Right before the order is finalized in the Check Out page the Customer class is filled with the user’s details, which are retrieved from the Membership and Profile providers and then passed to the FinalizeOrder method in the ShopManager class. The details from the Customer class are inserted into the database together with the other order details. So, even if a customer changes the shipping address manually, the goods will still be delivered to the address that was supplied during the ordering process. If you want to allow customers to change their order details for an order that has already been finalized, you need to implement this functionality. To make this secure, you could allow them to temporarily update their details, but postpone applying the changes to the order until they have confirmed an e-mail, for example. This way, you can be sure that the original user has requested and approved the change.
The Customer class has only seven public properties and only one method, its constructor, as shown in Figure 9-9.
Figure 9-9
283
Chapter 9
Most of the properties are pretty straightforward and don’t need an additional explanation. The only exception is the CustomerId property. This ID, implemented as a Guid, is not generated by the Customer class itself, but is retrieved from the ProviderUserKey property of the MembershipUser class. This is the unique ID for each user in the application, automatically generated and stored by the ASP.NET Framework. Because the data stored by the Membership and Profile providers is in the same database used by the rest of the WebShop application, it makes sense to reuse this unique key so it’s easy to retrieve related user details later based on this key.
All properties of the Customer class are read-only — to set their initial values, you need to pass them to the constructor of the class.
Out of the five classes in the business layer, only the ShopManager has methods that require data access to read from and write to the database. This data access is performed by the ShopManagerDB class, which is discussed in the next section.
The Data Access Layer
Two key elements make up the data access layer. First there is the database and its tables that store the data for the shop. The second part contains the methods in the ShopManagerDB class that uses stored procedures to get data in and out of the database. Because a fundamental knowledge of the database is important to understand how the data access methods work, the data model is discussed first, followed by the ShopManagerDB class.
The Data Model
For many of the operations that take place in the WebShop, a back-end database is used. This database, called WebShop.mdf and stored in the App_Data folder of the web site, stores data about products, orders, and categories. To understand how it all fits together, you should take a look at Figure 9-10, which shows the data model for the WebShop. It’s quite a simple model, with only four tables.
Figure 9-10
Figure 9-10 shows only the custom tables added for the WebShop; it does not list the tables that have been added by the Membership and Role providers. During run time, those tables are used by the ASP.NET Framework to authenticate users and determine their roles. They are not used for storing information about products, categories, or orders, other than the CustomerId in the OrderBase table that is used to determine what WebShop user placed the order. The following tables list each database table and their respective columns:
284
|
|
|
Wrox WebShop |
Product |
|
|
|
|
|
|
|
|
Column Name |
Data Type |
Description |
|
|
|
|
|
Id |
int |
The Unique ID of each product. This ID is generated |
|
|
|
automatically by the database each time a new product |
|
|
|
is inserted. |
|
Title |
nvarchar(100) |
The title of the product. The title is displayed on the |
|
|
|
product list and details pages of the product catalog. |
|
Description |
nvarchar(max) |
A longer description of the product. Although the |
|
|
|
nvarchar(max) data type, new in SQL Server 2005, |
|
|
|
allows you to store up to almost 2GB of information, |
|
|
|
the description is usually limited to a few K of text, |
|
|
|
describing the full product specs. |
|
Price |
money |
The price of the product. |
|
CategoryId |
int |
Each product is placed in a category that is displayed |
|
|
|
on the web site to allow for easy navigation of the |
|
|
|
product catalog. The names of the categories are stored |
|
|
|
in the Category table and the Product table stores only |
|
|
|
the primary key of that table as a foreign key. |
|
PictureUrlSmall |
nvarchar(255) |
A small picture showing the product. This image is |
|
|
|
shown in the shopping cart. |
|
PictureUrlMedium |
nvarchar(255) |
A medium-sized picture showing the product. This |
|
|
|
image is shown on the product list page. |
|
PictureUrlLarge |
nvarchar(255) |
A larger picture showing the product. This image is |
|
|
|
shown on the product details page. |
|
Deleted |
bit |
Indicates whether a product is still available on the |
|
|
|
web site. Because the product details are needed to |
|
|
|
display information about ordered products, a product |
|
|
|
can never be physically deleted from the database, |
|
|
|
Instead, it’s marked as “deleted.” |
Each product is linked to a category that is stored in the Category table:
Category
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of each category. This ID is generated |
|
|
automatically by the database each time a new category |
|
|
is inserted. |
Description |
nvarchar(100) |
The description of the category. The description of the |
|
|
category is used mainly in the navigation menu in the |
|
|
main shopping area to allow a user to select a category. |
285
Chapter 9
Once a customer has placed an order, the order data is stored in two tables — OrderBase and OrderDetails. The OrderBase table contains information that applies to the entire order, such as the order date and the customer that placed the order.
OrderBase
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of each order. This ID is generated |
|
|
automatically by the database each time a new order |
|
|
is inserted. The order ID is also communicated back |
|
|
to the customer as the order number. |
OrderDate |
datetime |
The date and time the order was placed. |
CustomerId |
uniqueidentifier |
The ID of the customer that placed the order. This is |
|
|
the primary key of the aspnet_Users table that has |
|
|
been added to the database to support the Role and |
|
|
Membership providers. |
FirstName |
nvarchar(50) |
The first name of the customer. |
LastName |
nvarchar(50) |
The last name of the customer. |
Street |
nvarchar(100) |
The customer’s shipping address. |
ZipCode |
nvarchar(20) |
The zip code of the customer’s address. |
City |
nvarchar(100) |
The city of the customer’s address. |
Country |
nvarchar(50) |
The country of the customer’s address. |
|
|
|
The OrderDetail table stores information about each product a customer has ordered. This table is linked back to the OrderBase table with its OrderBaseId column.
OrderDetail
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of each order detail record. This ID is |
|
|
generated automatically by the database each time a |
|
|
new order detail is inserted. |
OrderBaseId |
int |
The ID of the order to which this order detail belongs. |
ProductId |
int |
The ID of the ordered product. This ID is used to link |
|
|
back to the product table to get information such as |
|
|
the title and the description. |
Price |
money |
Because a product’s price may change after it has been |
|
|
ordered, the price is stored with the order details. This |
|
|
ensures that the total price for an order doesn’t change |
|
|
after it has been confirmed by a customer. |
Quantity |
int |
The number of items of the product the customer has |
|
|
ordered. |
|
|
|
286