ASP.NET 2.0 Instant Results
.pdfCustomer Support Site
Right below the class name in Figure 8-3 you see MustInherit Class. This means that the ContentBase cannot be instantiated directly, but that you must create an instance from a child class that inherits from ContentBase. This is exactly what you want, because there is no point in having a plain Content object somewhere on the site. Only the child classes, like Product and Download, are useful to display on the web site.
The public properties of the ContentBase classes were discussed briefly earlier, but the following table lists them again, together with their data type and a description:
Property |
Data Type |
Description |
|
|
|
CategoryId |
Integer |
The ID of the Category in the database to which the content |
|
|
item belongs. This ID holds only the ID of the deepest nested |
|
|
category in a hierarchy of categories. You see how this works |
|
|
later. |
Description |
String |
The description, or body text, of the content item. |
Id |
Integer |
The ID of the content item in the database. |
Title |
String |
The title of the content item. |
|
|
|
In addition to these properties, the ContentBase class defines one method called Save. The base class only defines the signature of the method and has been marked with the MustOverride keyword. This way, classes that inherit from ContentBase must implement the Save method. You see how this is done for the Product and Download classes later in the chapter.
The Product Class
The first class that inherits from ContentBase is the Product class. An instance of the Product class represents a real-world product that a customer may have bought in the Wrox Shop. On the Customer Support Site, the Product class is used to provide additional information about the product, like the product specifications.
In addition to the members inherited from ContentBase, this class has the members shown in Figure 8-4.
Figure 8-4
237
Chapter 8
The Product class extends the ContentBase class with three properties, described in the following table:
Property |
Data Type |
Description |
|
|
|
ImageUrl |
String |
A virtual path to an image that displays the |
|
|
product. |
Keywords |
String |
A comma-separated list of keywords describing |
|
|
the product. |
TagLine |
String |
A short and attractive description of the product. |
|
|
|
The Product class also adds three methods (of which one is overloaded) and two overloaded constructors. Although you can’t see it in Figure 8-4, the Save method is actually inherited from the ContentBase class, whereas the Get and Delete methods are specific to the Product class.
The following table lists the methods for the Product class. In addition to its two constructors (the New method), it also lists the Get, Save, and Delete methods, and two overloaded versions for
GetProductList.
Method |
Return Type |
Description |
|
|
|
Public Sub New () |
n/a |
The default constructor of the Product class. |
Public Sub New |
n/a |
An overloaded constructor that accepts a |
(ByVal id As Integer) |
|
product ID that is stored in a private variable. |
|
|
This overload is used when editing existing |
|
|
products. |
Public Shared Function |
Product |
This method retrieves a single product from the |
[Get] (ByVal id As |
|
database by calling a method with the same |
Integer) As Product |
|
name in the ProductDB class. |
Public Overrides |
n/a |
Saves a product in the database by calling into |
Sub Save () |
|
the ProductDB class. |
Public Shared Sub |
n/a |
Deletes a product from the database by calling |
Delete (ByVal id As |
|
into the ProductDB class. |
Integer) |
|
|
Public Shared Function |
DataSet |
Returns a list with all the available products in |
GetProductList () As |
|
the database. This method is used exclusively |
DataSet |
|
in the Management section. |
Public Shared Function |
DataSet |
Returns a list with products for the specified |
GetProductList (ByVal |
|
category. |
categoryId As Integer) |
|
|
As DataSet |
|
|
238
Customer Support Site
Because just like the Product class the Download class inherits from ContentBase, it should come as no surprise that the Download class has some methods in common with the Product class. The similarities and differences of the Download class are discussed next.
The Download Class
The Download class represents files that customers can download from the Wrox Hardware Customer Support Site. The downloads on the site are categorized using a three-level category hierarchy so it’s easy to find relevant download files. Just like the Product class, Download inherits from ContentBase and adds a few properties and methods of its own (see Figure 8-5).
Figure 8-5
The DownloadUrl property is a string holding a virtual path to a file that can be downloaded by a customer in the front end of the site. A content manager can upload a file in the Management section and then its path is saved in this property.
Just like the Product class, the Download class has Get, Save, and Delete methods and two overloaded constructors that work pretty much the same. Refer to the section “The Product Class” for a description of these methods.
In addition to these methods, the Download class has a method called GetDownloadList that returns a list with downloads as a DataSet.
The Faq Class
The Faq class represents a frequently asked question that is displayed on the web site and is stored in the customer support database. Though at first anFAQ may seem to be a good candidate to inherit from ContentBase as well, this isn’t the case. First of all, the FAQ doesn’t have a CategoryId. Also, the FAQ doesn’t have a title, but does have two Question properties and an Answer property. These differences make it hard (or at least very awkward) to have an FAQ inherit from ContentBase. Therefore, the Faq class is implemented as a stand-alone class with the members shown in Figure 8-6.
239
Chapter 8
Figure 8-6
The following table describes each of the public properties of the Faq class:
Property |
Data Type |
Description |
|
|
|
Answer |
String |
The answer to the question. |
QuestionLong |
String |
A longer and more detailed version of the |
|
|
question. |
Id |
Integer |
The ID of the content item in the database. |
QuestionShort |
String |
A short summary of the question used in the |
|
|
list with Frequently Asked Questions. |
Just like a Product and a Download, the Faq class has methods to get, save, and delete FAQs from the database. It also has two overloaded methods, outlined in the following table, to retrieve a list of FAQs from the database. One is used to get the questions based on a search term, and the other returns an unfiltered list of FAQs in the database.
Method |
|
|
Return Type |
Description |
|
|
|
|
|
Public Shared |
Function |
DataSet |
Returns a list with FAQs based on search |
|
GetFaqList |
(ByVal |
|
Term. This search term can holdsomething |
|
searchTerm |
As |
String) |
|
like “Printer AND 850 T5”. |
As DataSet |
|
|
|
|
Public Shared |
Function |
DataSet |
Returns a list with all available FAQs. This |
|
GetFaqList () As DataSet |
|
method is used exclusively in the Management |
||
|
|
|
|
section. |
|
|
|
|
|
Earlier you saw that the ContentBase class has a property CategoryId to link a content item to a category. To work with those categories, the Category class has been designed.
240
Customer Support Site
The Category Class
The Category class (see Figure 8-7) is used to retrieve and create categories in the database. These categories in turn are used to enable a user to quickly locate a product or download in the front end of the site.
Figure 8-7
The Category class has no public or private properties and exposes only shared and public methods (other than its private constructor) to retrieve categories from the database and to create new categories. The following table lists all three methods and describes their purpose:
Method |
Return Type |
Description |
|
|
|
Public Shared Sub |
n/a |
Creates a new category in the database. The |
CreateCategory (ByVal |
|
parentCategoryId passed to this method |
description As String, |
|
must contain the ID of an existing category in |
ByVal parentCategoryId |
|
the database, or must be less than one to |
As Integer) |
|
create a new root category. |
Public Shared Function |
DataSet |
Returns all the parent categories for a given |
GetCategoryPath (ByVal |
|
child category. This is useful to determine all |
categoryId As Integer) |
|
parent categories of a product or download |
As DataSet |
|
as only the deepest child’s CategoryId is |
|
|
saved. |
Public Shared Function |
DataSet |
Returns a list with categories as a DataSet |
GetCategoryList (ByVal |
|
with an ID and a Description column. When |
parentCategoryId As |
|
parentCategoryId is less than one, the root |
Integer) As DataSet |
|
categories are returned. Otherwise, the child |
|
|
categories for the given parent category are |
|
|
returned. |
|
|
|
Now that you have seen all the classes that make up the business layer, it’s time to look at the classes and database tables that make up the data access layer.
The Data Access Layer
Because many of the classes in the business layer work with data that is stored in the database, it should come as no surprise that for most of those classes there is an associated class in the data access layer (in the DataAccess folder, which in turn is located in the special App_Code folder in the root of the site) with a name ending in DB. The only exception is the ContentBase class. Being the parent for the Product and
241
Chapter 8
Download classes, the ContentBase class does not have implementation code that requires database access, so it also doesn’t need a companion ContentBaseDB class. The other four classes that do access the database are described in the sections that follow.
The ProductDB Class
The ProductDB class implements the same four methods you saw earlier for the Product class. However, the methods in the ProductDB class, shown in Figure 8-8, perform the real work in getting the data in and out of the database. Notice there isn’t an overloaded version of the GetProductList in this database class. The Product class does have two overloaded versions, but calls the same, single method in the ProductDB class.
Figure 8-8
The methods listed in Figure 8-8 are discussed in the following table:
Method |
Return Type |
Description |
|
|
|
Public Shared Sub Delete |
n/a |
Deletes a product from the database. |
(ByVal id As Integer) |
|
|
Public Shared Function |
Product |
Retrieves a single product instance |
[Get] (ByVal id As Integer) |
|
from the database. Returns Nothing |
As Product |
|
when the requested product could not |
|
|
be located. |
Public Shared Function |
DataSet |
Returns a list with products from the |
GetProductList (ByVal |
|
database. When CategoryId is -1, all |
categoryId As Integer) |
|
products are returned. |
As DataSet |
|
|
Public Shared Sub Save |
n/a |
Saves the product in the database. This |
(ByVal the Product As |
|
is the only instance method, because |
Product) |
|
all the others are marked as Shared. |
|
|
This instance method saves the under- |
|
|
lying values of the product in the |
|
|
database. |
The DownloadDB class, which has a lot in common with the ProductDB class, is discussed next.
242
Customer Support Site
The DownloadDB Class
Just as the Product and Download classes are very similar, so are the ProductDB and DownloadDB classes. This means that this class implements similar Get, Save, Delete, and GetDownloadList methods, as illustrated in Figure 8-9.
Figure 8-9
The behavior and description for most of these methods are identical to those of the ProductDB class. Refer to the table with methods for the ProductDB class for a description, replacing Product with Download in any of the names and descriptions you see. The only exception in the method names is GetDownloadList. Similar to GetProductList, this method returns a list with downloads as a DataSet.
The FaqDB class
Although the Faq class does not inherit from ContentBase, it does implement the same methods that the Product and Download classes have. Therefore, the FaqDB class (see Figure 8-10) implements the methods Get, Save, Delete, and GetFaqList.
Figure 8-10
In addition to those familiar methods, the FaqDB class also has a BuildWhereClause method. This method, marked as Private in the code so it’s not accessible from outside the FaqDB class, accepts a search term and returns a fully formatted WHERE clause that can be used in a stored procedure. Although this potentially opens up your code for SQL injection attacks, this method deploys some defensive code to avoid this security risk. You see how this works later.
The CategoryDB class
The final class in the data access layer you should look at is the CategoryDB class. Just as its counterpart in the business layer, this class implements the three methods depicted in Figure 8-11 for working with categories.
243
Chapter 8
Figure 8-11
These methods are described in the following table:
Method |
Return Type |
Description |
|
|
|
Public Shared Sub |
n/a |
Creates a new category in the database. |
CreateCategory (ByVal |
|
The parentCategoryId passed to this |
description As String, |
|
method must contain the ID of an existing |
ByVal parentCategoryId |
|
category in the database, or must be less |
As Integer) |
|
than one to create a new root category. |
Public Shared Function |
DataSet |
Returns all the parent categories for a |
GetCategoryPath (ByVal |
|
given child category. This is useful to |
categoryId As Integer) |
|
determine all parent categories of a |
As DataSet |
|
product or download, because only the |
|
|
deepest child categoryId is saved. |
Public Shared Function |
DataSet |
Returns a list with categories as a DataSet |
GetCategoryList (ByVal |
|
with an ID and a Description column. |
parentCategoryId As |
|
When parentCategoryId is less than |
Integer) As DataSet |
|
one, the root categories are returned. |
|
|
Otherwise, the child categories for the |
|
|
given parent category are returned. |
|
|
|
In addition to the classes in the DataAccess folder, the data access layer also contains the actual database that consists of four database tables and a number of stored procedures.
The Data Model
The database for the Custom Support Site features four tables, 16 stored procedures, and two user-defined functions. Some of the tables in the database are related to each other, as shown in Figure 8-12.
Both the Product and the Download tables have a relation with the Category table through their CategoryId column. However, you should also note that the Category table has a relation with itself. The ParentCategoryId column is related to the Id column in the same table. This way, a category can be related to another category, called its parent category, therefore creating a hierarchy or tree structure of categories. To retrieve the hierarchical data from the database, the application makes use of Common Table Expressions, which are discussed later.
Although the names of the tables and their columns are pretty self-explanatory, the following tables list each of them and describe their purpose and data type.
244
Customer Support Site
Figure 8-12
The Product Table
This table describes the contents of the Product table in the Customer Support Site database:
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of the product in the database. The ID |
|
|
is generated automatically whenever a new product is |
|
|
inserted. |
Title |
nvarchar(100) |
The title of the product. |
TagLine |
nvarchar(MAX) |
A longer title or a subtitle for the product that can also |
|
|
hold a short marketing message for the product. |
Description |
nvarchar(MAX) |
The full product description, holding the product’s |
|
|
specification, for example. |
CategoryId |
int |
The ID of the category to which the product belongs. |
ImageUrl |
nvarchar(255) |
The virtual path to an image showing the product. |
Keywords |
nvarchar(200) |
Holds a comma-separated list with keywords applicable |
|
|
to the product. |
The next table describes the five columns of the Download table in the database.
The Download Table
The Download and the Product tables have a lot in common. The columns that these tables share map exactly to the public properties of the parent ContentBase class that Product and Download inherit from.
245
Chapter 8
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of the download in the database. |
|
|
The ID is generated automatically whenever a new |
|
|
download is inserted. |
Title |
nvarchar(100) |
The title of the download, briefly describing the file |
|
|
that can be downloaded. |
Description |
nvarchar(MAX) |
The full download description. |
CategoryId |
int |
The ID of the category to which the download |
|
|
belongs. |
DownloadUrl |
nvarchar(255) |
The virtual path to a downloadable file for this |
|
|
download. |
|
|
|
It is possible to simulate inheritance in the database by creating a generic ContentBase table that stores information for both a product and a download. Then the other tables store their own data (such as ImageUrl for a product and DownloadUrl for the Download table) together with a foreign key pointing to the ContentBase table that holds the base data for the record like the Title and Description.
However, such a solution can result in a messy table structure pretty quickly. Also, the extra amount of work it takes to insert data in two tables and to keep those tables in sync makes this solution a less attractive alternative. Therefore, it was decided to duplicate the shared columns in both the Product and the Download tables.
The Faq Table
The Faq table stores the data for the Frequently Asked Questions and has the following four columns:
Column Name |
Data Type |
Description |
|
|
|
Id |
int |
The unique ID of the FAQ in the database. The ID |
|
|
is generated automatically whenever a new FAQ is |
|
|
inserted. |
QuestionShort |
nvarchar(200) |
A short version of the question of the FAQ. |
QuestionLong |
nvarchar(MAX) |
A longer version of the question, possibly providing |
|
|
more background information about the question. |
Answer |
nvarchar(MAX) |
The answer to the question. |
|
|
|
Now, on to the final table in the Customer Support Site database: the Category table.
The Category Table
The Category table stores the categories used throughout the site. This table has a relation to itself through its ParentCategoryId column that is related to the Id column.
246