Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP.NET 2.0 Instant Results

.pdf
Скачиваний:
67
Добавлен:
17.08.2013
Размер:
11.03 Mб
Скачать

The Bug Base

web project. This allows for even further abstraction of the code and promotes reuse. However, for many applications this new code folder is all you need to create well-designed applications.

The remainder of this section discusses the business layer and the data access layer. The presentation layer is discussed in the section “Code and Code Explanation.”

The Business Layer

The business layer of the Bug Base is located in the BusinessLogic folder inside the App_Code folder in the root of the application. It consists of eight classes and one enumeration, each of which is discussed in detail in the next section.

Bug

The Bug class, shown in Figure 12-7 and located in Bug.vb in the BusinessLogic folder, is the main entity in the application. It represents the bug that is filed through the web interface and stored in the database.

Figure 12-7

The Bug class exposes only properties; it has no behavior in terms of methods, other than two constructors. All actions you can perform on a bug, such as filing, changing, and searching lists of bugs, are carried out by the BugManager class.

The Bug class has the following properties:

Property

Type

Description

 

 

 

Application

NameValue

A NameValue object holding the Id and the

 

 

Description for the application the bug is filed

 

 

against.

CreatedDateAndTime

DateTime

The date and time the bug was filed.

 

 

 

397

Chapter 12

Property

Type

Description

 

 

 

CreateMemberId

Guid

The unique ID of the user who filed the bug.

Title

String

A short description of the bug, used to quickly

 

 

identify bugs.

Description

String

The full description of the bug, possibly including

 

 

a detailed set of instructions to reproduce the

 

 

behavior and describe the problem.

Feature

NameValue

A NameValue object holding the Id and the

 

 

Description for the feature of the application the

 

 

bug is filed against.

Frequency

NameValue

The Frequency describes how often a bug occurs

 

 

and how many users are likely to run into it.

Id

Integer

Each bug is represented by a unique ID. The ID is

 

 

automatically generated by the Bug table in the

 

 

database whenever a new bug is inserted.

Priority

Integer

The Priority of a bug often determines the order

 

 

in which bugs should be fixed.

Reproducibility

NameValue

The Reproducibility describes if and how often

 

 

the bug can be reproduced.

Severity

NameValue

The Severity describes the impact of a bug,

 

 

ranging from usability issues to loss of data and

 

 

application crashes.

Status

NameValue

The Status indicates the current state of the bug.

 

 

A status in turn can determine if the bug should

 

 

be treated as closed. Refer to the Status table in the

 

 

database for a full list of all the Status items.

UpdatedDateAndTime

DateTime

The date and time the bug was last updated.

UpdateMemberId

Guid

The unique ID of the user who last updated the bug.

 

 

 

The Bug class also has two constructors:

Property

Description

 

 

New ()

Creates a new Bug object with all properties set to their

 

default values.

New (ByVal id As Integer)

Creates a new Bug object with most properties set to their

 

default values. The id that is passed to the constructor is set

 

as the Id of the bug.

Because the Bug class has only properties, it cannot perform any actions, such as saving itself in the database. Instead, these actions are carried out by the BugManager class.

398

The Bug Base

BugManager

The BugManager class (see Figure 12-8) is responsible for all actions on bugs. It has methods to insert new and change existing bugs and to retrieve lists of bugs that match specific search criteria. The BugManager class exposes two read-only properties called Count and MemberId. The Count property returns the number of bugs currently held by the BugManager in the private field _theBugList. The MemberId property contains the current member’s ID and is used to check access rights in the business and data access layers.

Figure 12-8

The BugManager class also has the following methods:

Method

Return Type

Description

 

 

 

InsertUpdateBug

Integer

Saves a fully populated Bug object. It does this by

(ByVal theBug As Bug)

 

calling InsertUpdateBug on the BugManagerDB

 

 

class and passing it the instance of the bug. The

 

 

Integer returned from this method is the new or

 

 

current ID of the bug in the database.

GetBug (ByVal

Bug

Retrieves a bug based on the ID passed to this

id As Integer)

 

method. Returns Nothing when the bug could not

 

 

be found or the user doesn’t have permission to

 

 

view it.

GetBugList

List(Of Bug)

Retrieves a list of bugs optionally based on search

(+ one additional

 

criteria and sorted on one of the bug’s properties.

overload)

 

The list that is returned is actually a strongly typed

 

 

list of bugs, using the new generics feature of the

 

 

.NET Framework. Each of the two overloads is

 

 

discussed in greater detail in the section “Code and

 

 

Code Explanation.”

399

Chapter 12

Almost all of the methods in the BugManager class do nothing more than delegate their responsibility to a method with the same name in the BugMagagerDB class. The only exception is the GetBugList method that also sorts the list of bugs by using the BugComparer class, which is discussed in the next section.

BugComparer

The BugComparer class implements the IComparer(Of Bug) interface, which enables sorting of objects in a list that uses generics. It implements the only required method, Compare, and has a constructor that accepts the name of a Bug property to sort on as a parameter. The Compare method compares the two Bug objects passed to it and returns an integer indicating whether the first Bug object is less than, equal to, or greater than the second Bug object. Because of its tight relation with sorting bugs in the BugManager, the BugComparer is implemented as a nested class in the BugManager class, visible in Figure 12-8.

CommentManager

When users update an existing bug, they can add a comment to provide additional information. These comments are handled by the CommentManager class, which is shown in Figure 12-9.

Figure 12-9

This is a very simple class to insert and retrieve comments and has only two methods:

Method

Return Type

Description

 

 

 

GetCommentList (ByVal

DataSet

Returns a list of comments for the

bugId As Integer)

 

requested bug by calling into the

 

 

CommentManagerDB class.

InsertComment (ByVal

n/a

Inserts a new comment in the Comment

bugId As Integer, ByVal

 

table in the database and associates it

theBody As String,

 

with the bugId passed to this method.

ByVal theMemberId As Guid)

 

 

To get the various lists, such as Frequency and Severity in the presentation layer, the business layer has a ListManager class, which is discussed next.

ListManager

The ListManager class is responsible for retrieving lists that are displayed on the web site. It has nine public shared methods (see Figure 12-10) to retrieve applications, features, and lists of other bug properties, such as the Severity, Reproducibility, Status, and Frequency. These lists are used in the presentation layer to

400

The Bug Base

fill drop-down menus. Because it has only shared methods, the constructor of the class is hidden by marking it Private. This prevents you from accidentally creating instances of the ListManager class. To use the methods in the class, you can simply call them on the class name.

Figure 12-10

The ListManager class caches most of these lists in the ASP.NET cache using a SqlCacheDependency, so there is no need to hit the database every time they are needed. Because these lists are used quite often, this greatly increases the application’s performance. You see later how this works. The following table lists the public methods that are used for working with Applications and Features:

Property

Return Type

Description

 

 

 

GetApplicationItems

DataSet

Returns a list with Applications as a DataSet

 

 

(+ two additional overloads). The DataSet

 

 

contains two columns: the ID and the

 

 

Description of the item in the database. The

 

 

overloads are used to limit the list to active

 

 

applications, or to applications to which a

 

 

user has access.

GetApplicationDescription

String

Returns the user-friendly name of an

(ByVal applicationId As

 

application.

Integer)

 

 

GetFeatureItems

DataSet

Returns a list with Feature items as a DataSet.

 

 

The DataSet contains two columns: the ID

 

 

and the Description of the item in the

 

 

database.

The methods that return the lists for Frequency, Reproducibility, Severity, and Status all follow the same pattern. They return the requested items as a DataSet that has an ID and a Description column. Under the hood, they call the private method GetListItems and pass it a custom ListType enumeration (defined in the BusinessLogic folder in the file called ListType.vb) to indicate the type of list to retrieve. The GetListItems method then calls into the data access layer to get the items from the database.

401

Chapter 12

MemberManager

The MemberManager class (see Figure 12-11) is responsible for changing the user’s access rights in the database. Because the ASP.NET 2.0 Framework already provides a lot of ready-to-use classes to work with users and security settings in your application, the implementation of the MemberManager is very simple.

Figure 12-11

The MemberManager class has two public subs that allow you to assign and unassign a user to a specific application:

Property

Description

 

 

AssignMemberToApplication

Assigns a member indicated by memberId to the

(ByVal memberId As Guid,

requested application.

ByVal applicationId As Integer)

 

UnAssignMemberFromApplication

Removes a member from the requested application.

(ByVal memberId As Guid,

 

ByVal applicationId As Integer)

 

Both these methods call a private member in the MemberManager class called ChangeMember ApplicationBinding and pass it either True or False to indicate whether the user should be added to or removed from the application.

NameValue

Many of the properties of a bug in the database, such as the Severity and the Reproducibility, are actually foreign keys to other tables in the BugBase database. These tables are often referred to as domain tables. This means that only the ID is stored with the bug. To the end-user of the application, these IDs are meaningless. To display the friendly name of these properties in the user interface, the Bug class exposes these properties as NameValue objects. The NameValue class (see Figure 12-12) has a Value property that holds the underlying ID in the database. The Name property exposes the friendly name.

You can create a new NameValue by calling the default constructor and then set the Name and Value properties individually. Alternatively, you can call the overloaded constructor that accepts values for the Name and Value properties as arguments.

SearchCriteria

The SearchCriteria class (see Figure 12-13) is used by the BugManager in the GetBugList methods. The GetBugList allows you to search for bugs that match a comprehensive list of search criteria.

402

The Bug Base

Figure 12-12

Figure 12-13

Instead of passing each of these criteria separately to this method, you can pass a single SearchCriteria object that exposes public properties for each of the criteria. The GetBugList method examines each of these properties and builds up the criteria parameters that are passed to the database. This is explained in more detail when the BugManagerDB class is examined in the next section.

The Data Access Layer

The data access layer in the Bug Base is designed to work with SQL Server only, because it uses types you find in the System.Data.SqlClient namespace, like the SqlConnection and SqlCommand objects. However, to make it easier to switch databases later in the lifetime of the application, none of the methods in the layer returns data provider–specific types. Instead, each of these methods returns standard types like a DataSet, or custom generics lists, like the bug list. If you decide to change the database you’re using, all you need to change is the methods in the data access layer. As an alternative to changing the data access layer each time you want to target a different database, you can also recode the data access layer using the provider factories pattern that you saw in Chapter 6.

403

Chapter 12

The only exception to this rule is the GetList method in the ListManager class. This method uses SqlCacheDependency classes to cache data from the database. The cache is invalidated whenever the underlying table in the database changes. SQL cache invalidation only works with SQL Server, so if you decide to switch databases, you’ll need to modify the GetList method by either removing the code responsible for caching altogether or by implementing a different caching strategy.

The use of DataSets in the data access layer causes some overhead when compared to lightweight objects like the DataReader. However, this overhead can be minimized by implementing a thorough caching strategy, as is done by the methods in the ListManager class. By creating a SqlCacheDependency on the relevant tables in the database, you can in fact increase performance. All of the domain list tables, such as Severity and Reproducibility, are cached as DataSets in memory, so there is no need to hit the database each time you need them. Only when the table is changed — something that won’t happen very often — is the item removed from the cache and needs to be reconstructed. This greatly reduces the number of calls made to the database, something that cannot be accomplished using DataReader objects.

Before discussing the data access layer, you should take a look at the design of the database first. Because each of the methods in the data access layer talks directly to the SQL Server 2005 database, it’s important to understand how the database is designed. Figure 12-14 displays the database diagram for the Bug Base, showing most of its tables and relations.

Figure 12-14

Figure 12-14 does not show the tables that have been added for the ASP.NET 2.0 Membership and Role providers, except for the aspnet_Users table that has relations with other tables in the Bug Base. The following table discusses each table in the database and its intended purpose:

404

 

 

The Bug Base

 

 

 

 

Table Name

Description

 

 

 

 

Application

Holds a list with all the applications you can file bugs against. The column

 

 

IsActive determines whether the application is still in use.

 

aspnet_Users

This table is added by the aspnet_regsql.exe tool when you enable the

 

 

database for Membership and Roles. It holds the user accounts for the Bug

 

 

Base application. The UserId, a GUID, is used to link other tables to this

 

 

table.

 

Bug

The logged bugs are stored in this table. Besides a Title, a Description, and the

 

 

date the bug was created and updated, this table largely consists of foreign

 

 

keys pointing to domain list tables.

 

Comment

Holds comments that users can add to existing bugs. The CreateMemberId

 

 

has a link to the aspnet_Users table to keep track of who added the comment.

 

Feature

Features are the main parts that make up your application. A bug should

 

 

be logged against a specific feature, to make it clearer where the bug occurs

 

 

and who’s responsible for it. A feature is always tied to an application, so

 

 

the Feature table has an ApplicationId column that points back to the

 

 

Application table.

 

Frequency

The frequency of a bug defines how often, or by how many users, a bug

 

 

will be encountered. This table holds a list with possible options that the

 

 

user can choose from.

 

MemberApplication

Users should not be able to log bugs against any arbitrary application. An

 

 

Administrator can assign members to a specific application through the

 

 

web application. This assignment is stored in the junction table Member-

 

 

Application.

 

Reproducibility

The reproducibility of a bug defines whether a bug is reproducible at all,

 

 

and if so, how often. Just as the Frequency and Feature tables, this is a

 

 

domain list table that stores the description for each item with a primary

 

 

key. This key is then used as a foreign key in the Bug table.

 

Severity

The severity describes the impact of the bug. This domain list table holds

 

 

the various options for this bug property.

 

Status

This table holds a list with possible status options for a bug. The ClosesBug

 

 

column determines whether the bug becomes inactive with a specific status.

 

 

This is the case for a status such as Deferred, Closed, or Not a Bug.

 

 

 

In addition to these 10 tables, the database also contains a number of stored procedures. Many of these procedures follow a strict naming pattern:

sprocTableNameSelectSingleItem

sprocTableNameSelectList

sprocTableNameInsertUpdateSingleItem

405

Chapter 12

The first procedure selects a single record from a table referred to by TableName. The WHERE clause always uses at least the primary key of the table to limit the number of records to a maximum of 1, as in the following procedure that queries a feature from the database:

CREATE PROCEDURE sprocFeatureSelectSingleItem

@id int

AS

SELECT

Id,

Description,

ApplicationId

FROM

Feature

WHERE

Id = @id

The *SelectList procedures query a list of related items from the database, such as a list of features, bugs, applications, and so on. They often look very similar to the SelectSingleItem bugs in terms of the columns they return, but they don’t use the primary key of the table in the WHERE clause, and they often sort the result set using an ORDER BY clause.

All the *InsertUpdate procedures are capable of both inserting new and updating existing items in the database. They do that by looking at the @Id parameter passed to this procedure. If that parameter — which represents the primary key of the record in the table — is null, a new record is inserted. Otherwise, an existing record is updated where the @id parameter is used in the WHERE clause as demonstrated in the following code:

CREATE PROCEDURE sprocFrequencyInsertUpdateSingleItem

@id int = null, @description nvarchar (100)

AS

DECLARE @returnValue int

IF (@id IS NULL) -- New Item

BEGIN

--Insert the item here and return its new Id

--Insert code is left out of the example SELECT @returnValue = Scope_Identity()

END

ELSE

BEGIN

--Update the item here and return the existing Id

--Update code is left out of the example

SELECT @returnValue = @id

END

- Return the new or existing Id to the calling code RETURN @returnValue

406