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

ASP.NET 2.0 Instant Results

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

The Bug Base

(CType(e.ReturnValue, DataSet)).Tables(0).Rows.Count > 0 lstFeature.Visible = featureListVisible

lblFeature.Visible = featureListVisible End Sub

The first line of code in this method looks at the number of rows in the table in the DataSet, exposed by the ReturnValue property of the e argument. When the number is greater than zero, feature ListVisible is True and the list is visible. Otherwise, the list is made invisible.

All the other drop-down controls don’t need additional code to function. Because they are always visible, the ASP.NET Framework binds them when the page loads. And with their EnableViewState property set to True (the default), they automatically maintain their state so there is no need to bind them again on postback.

There is, however, one control that must be bound manually and that’s the GridView. There is no need to bind that control on every page load or postback, because you should be able to make a detailed selection first. Once the selection is complete, you should click the Report button to get the selected bugs from the database. The Click event of the Report button causes the following chain of events to fire.

First, the LoadData method is called:

Private Sub LoadData()

GridView1.Visible = True

GridView1.DataSourceID = “odsBugList”

GridView1.DataBind()

End Sub

In this method the GridView is made visible and then its DataSourceID is set to the ID of the odsBugList that is defined in the markup of the page. Finally, by calling DataBind() on the GridView control, odsBugList gets the data from the database so it can be displayed on the page. Easy as that last sentence sounds, it’s actually quite a complicated process. You need to look at a few sections in more detail to understand how this works.

First, there is the ObjectDataSource control in the markup that is set up to call GetBugList in the business layer:

<asp:ObjectDataSource ID=”odsBugList” runat=”server” SelectMethod=”GetBugList” SortParameterName=”sortExpression” TypeName=”BugManager” EnableViewState=”False”>

<SelectParameters>

<asp:Parameter Name=”sortExpression” Type=”String” /> <asp:Parameter Name=”searchCriteria” />

</SelectParameters>

</asp:ObjectDataSource>

In the discussion of the BugManager class you learned that the GetBugList has two overloads. The ObjectDataSource is targeting the overload with two parameters: the first is a string holding the name of a property the bug list should be sorted on, and the other is a SearchCriteria object that holds a range of criteria that the list should be filtered on:

Public Function GetBugList(ByVal sortExpression As String, _

ByVal searchCriteria As SearchCriteria) As List(Of Bug)

427

Chapter 12

How is ASP.NET able to pass the correct parameters to this method? If you look at the definition for the

ObjectDataSource you see two <asp:Parameter> attributes defined in the <SelectParameters> section. The first one holds the name of the argument of the SelectMethod that is used when sorting. Here the GridView and the DataSource play nice together. Whenever you click one of the column headings of the GridView, the SortExpression of the GridView’s column is passed into the SelectMethod defined on the DataSource. Eventually, this sortExpression ends up in the GetBugList method where it’s used to sort the list of bugs. This is examined in more detail later.

The second <SelectParameter> — called searchCriteria — is set up in the code-behind for the page. To see how that object is created and passed to the GetBugList, you first need to understand how the

ObjectDataSource sets up the BugManager it’s going to use. Whenever the ObjectDataSource tries to bind itself to its DataSource (triggered by calling DataBind on the GridView in the LoadData() method), the DataSource fires its ObjectCreating event. Inside this event, you can assign the

BugManager to the DataSource object:

Protected Sub odsBugList_ObjectCreating(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.ObjectDataSourceEventArgs) _ Handles odsBugList.ObjectCreating

e.ObjectInstance = myBugManager End Sub

The myBugManager object is defined as a private variable at the top of the code-behind for the Reports page and instantiated in Page_Load. In other circumstances there is often no need for this additional code; the ObjectDataSource itself is able to figure out how to create a new instance of the object it’s bound to. However, in the Reports page you need access to an instance of the BugManager class to get the total number of bugs it’s holding, using the Count property.

Once the ObjectDataSource is done with the Creating method, it fires its Selecting event. This event fires right before the data is retrieved, so it’s a perfect location to set up the values for the arguments that are going to be passed to GetBugList. In the case of the Reports page, a searchCriteria object is passed:

Protected Sub odsBugList_Selecting(ByVal sender As Object, _

ByVal e As System.Web.UI.WebControls.ObjectDataSourceSelectingEventArgs) _ Handles odsBugList.Selecting

Build up a SearchCriteria object and set its properties Dim searchCriteria As SearchCriteria = New SearchCriteria()

Set the Application when selected

If Not lstApplications.SelectedValue = “” Then

searchCriteria.ApplicationId = Convert.ToInt32(lstApplications.SelectedValue) End If

‘ Set the Feature when selected

For Each myItem As ListItem In lstFeature.Items If myItem.Selected = True Then

searchCriteria.AddFeature(myItem.Value) End If

Next

... other properties are set here

Set Start Date

428

The Bug Base

If Not calStartDate.SelectedDate = DateTime.MinValue Then searchCriteria.StartDate = calStartDate.SelectedDate

End If

... other properties are set here

Assign the SearchCriteria object to the InputParameters

collection of the DataSource

e.InputParameters.Item(1) = searchCriteria End Sub

In this method a new SearchCriteria object is instantiated. Then the values of each of the controls on the page used for filtering are added to the SearchCriteria object. You’ll notice that for some properties a method is used that starts with Add. This method adds the value passed to it to an internal comma-separated list. So, if you selected the features 1, 4, and 16 in the list, the internal variable would hold 1,4,16. When the stored procedure for the GetBugList method is explained, you discover how this list is used.

Once the properties for the ObjectDataSource are set up, the object is assigned to the InputParameters collection of the ObjectDataSourceSelectingEventArgs object, using e.InputParameters.Item(1) = searchCriteria.

The next step in the process is the actual call to GetBugList in the business layer. This method simply checks if the internal_memberId field is valid, and then calls into the BugManagerDB class, passing up the memberId and the searchCriteria object:

_theBugList = BugManagerDB.GetBugList(searchCriteria, _memberId)

The GetBugList method in the data access layer and its associated stored procedure are probably the most complicated pieces of code in the application, so again they are explained in great detail. First take a look at the beginning of the function:

Public Shared Function GetBugList(ByVal searchCriteria As SearchCriteria, _ ByVal memberId As Guid) As List(Of Bug)

Dim sql As String = “sprocBugSelectList”

Dim theBugList As New List(Of Bug) ‘ BugList to hold all the bugs

Try

Using myConnection As New SqlConnection(AppConfiguration.ConnectionString) Dim myCommand As SqlCommand = New SqlCommand(sql, myConnection) myCommand.CommandType = CommandType.StoredProcedure

The syntax As New List(Of Bug) creates a new strongly typed list that can hold Bug objects. This is part of the new generics feature in .NET 2.0 languages that allow you to quickly create custom strongly typed lists and collections without the need to write a lot of code. This code simply creates a new List, which is basically an array that can hold only Bug items and whose size automatically changes when you add new items to it.

Next, the properties of the searchCriteria object are added as parameters on the SqlCommand object:

If searchCriteria IsNot Nothing Then ‘ Add the Application Id

If searchCriteria.ApplicationId <> -1 Then

429

Chapter 12

myCommand.Parameters.AddWithValue(“@applicationId”, _ searchCriteria.ApplicationId)

End If

This code creates a new parameter called @applicationId and assigns it the value held in the Application Id property of the searchCriteria if it has been set. This process is repeated for each of the properties of the SearchCriteria class. Notice that the comma-separated list of values for properties like Status and Severity are simply passed as strings to the stored procedure:

‘ Add the severity, which can be a comma separated list If Not searchCriteria.Severity = String.Empty Then

myCommand.Parameters.AddWithValue(“@severity”, searchCriteria.Severity) End If

Then a temporary bug is declared and the connection is opened:

Dim theBug As Bug ‘Temp bug to add to the BugList

myConnection.Open()

The bugs are retrieved from the database using a SqlDataReader that is executed with the following code:

Using myReader As SqlDataReader = _ myCommand.ExecuteReader(CommandBehavior.CloseConnection)

While myReader.Read()

Add bugs retrieved from the database to the list here.

This is shown later

End While myReader.Close()

End Using

The code for the stored procedure that is used to feed this SqlDataReader is quite lengthy, so it doesn’t appear here; rather, this section focuses on the important bits. The complete code for the BugBase application is available on the companion CD-ROM and can also be downloaded from www.wrox.com. The first important thing you’ll notice in the procedure is the use of the dbo.fnSplit function in some of the JOINs:

LEFT OUTER JOIN dbo.fnSplit(@feature, ‘,’) joinFeature ON Bug.FeatureId LIKE

joinFeature.[value]

Remember that some of the SearchCriteria properties were actually comma-separated strings with values? This is where those come into play. Here you’ll discover how it works for the Bug’s feature, but the principle applies to each of the other properties that use the fnSplit function.

To select the bugs that are filed for one or more features, you would normally use a SQL IN statement like this:

SELECT Bug.Id FROM Bugs WHERE Bug.FeatureId IN (1, 4, 16)

430

The Bug Base

This selects all the bugs that are filed for either feature 1, 4, or 16. This IN statement cannot be used in a stored procedure directly because SQL Server does not support parameterized IN filters. One way to work around that is to create your SQL statement dynamically in the stored procedure and then use EXEC to execute it. However, in addition to the messy code this creates, it also opens up SQL Server to all kinds of SQL injection attacks if no additional security measures are taken.

Instead, you should use a function that accepts the comma-separated list of IDs and returns it as a table object that can be used in a JOIN. If you think of the result of the fnSplit function as a table that has one column called value that holds three rows with 1, 4, and 16, the JOIN becomes a lot easier to understand:

LEFT OUTER JOIN FeatureTempTable ON Bug.FeatureId LIKE

FeatureTempTable.[value]

This JOIN links the list of bugs to the Features in the temp table returned by the function.

You’ll find the dbo.fnSplit function — taken directly from Microsoft’s MSDN web site — under the Functions node of the Database Explorer in Visual Web Developer. The function has inline comments describing how it works.

The WHERE clause in the procedure eventually filters the bugs that match the items in the temp tables:

AND ((Bug.FeatureId LIKE joinFeature.[value]) OR (@feature IS NULL))

This statement filters the bug list to those that have a direct match to a record in the temp for Features. If the parameter @feature is null, no filtering takes place and all records are returned.

This process is repeated for the other bug properties such as the Severity and Status, resulting in a sophisticated filter on the bug list.

Once the procedure is done selecting the right bugs from the Bug table, it returns a result set back to the SqlDataReader in the GetBugList method. The code then loops through each of the items in the SqlDataReader, creates a new instance of a Bug object, sets all of its properties by filling them with data from the database, and then adds the new Bug object to the BugList, as illustrated by the following highlighted code:

Using myReader As SqlDataReader = _ myCommand.ExecuteReader(CommandBehavior.CloseConnection)

While myReader.Read()

theBug = New Bug(myReader.GetInt32(myReader.GetOrdinal(“Id”))) theBug.Title = myReader.GetString(myReader.GetOrdinal(“Title”)) theBug.Description = myReader.GetString(myReader.GetOrdinal(“Description”)) ‘ ... other properties are set here

theBug.Application = New NameValue(myReader.GetInt32( _ myReader.GetOrdinal(“ApplicationId”)), myReader.GetString( _ myReader.GetOrdinal(“ApplicationDescription”)))

theBug.UpdatedDateAndTime = _ myReader.GetDateTime(myReader.GetOrdinal(“UpdatedDateAndTime”))

theBugList.Add(theBug) End While myReader.Close()

End Using

Return theBugList

431

Chapter 12

This code is very similar to the code that retrieved a single bug from the database. The only difference here is that the bug itself is not returned, but that it is added to the BugList first, which is then returned at the end of the function.

As soon as the BugList is returned from the data access layer back to the business layer, the remainder of the code in the GetBugList method fires:

_theBugList = BugManagerDB.GetBugList(searchCriteria, _memberId)

‘ If there is more than 1 item in the list , sort it. If _theBugList.Count > 1 Then

_theBugList.Sort(New BugComparer(sortExpression)) End If

Return _theBugList

The Sort method of the generics List class expects a generic class that implements IComparer. The BugComparer class is such a class and implements Compare, the only method in the interface. This method should return an Integer indicating whether an object is less than, equal to, or greater than another object. The Compare method contains the following code:

Public Function Compare(ByVal a As Bug, _

ByVal b As Bug) As Integer Implements IComparer(Of Bug).Compare Dim retVal As Integer = 0

Select Case _sortColumn.ToLower() Case “id”, “”

retVal = a.Id.CompareTo(b.Id) Case “title”

retVal = String.Compare(a.Title, b.Title, _ StringComparison.InvariantCultureIgnoreCase)

Case “feature”

retVal = String.Compare(a.Feature.Name, b.Feature.Name, _ StringComparison.InvariantCultureIgnoreCase)

‘ ... other properties are compared here

Case “updateddateandtime”

retVal = DateTime.Compare(a.CreatedDateAndTime, b.CreatedDateAndTime) End Select

Dim _reverseInt As Integer = 1

If (_reverse) Then

_reverseInt = -1

End If

Return (retVal * _reverseInt)

End Function

The method is designed to accept two instances of a bug object, Bug a and Bug b, which are passed to this method. The private variable_sortColumn holds the name of the property that the bugs should be compared on. This takes place in the Select Case block where each of the comparable properties of a bug has its own Case block. Instead of trying to figure out which property is larger than the other with

432

The Bug Base

custom code, this code uses the Compare method of the underlying data types. Note that when NameValue objects are compared, the Name property is used and not the Value. The user is expecting the list to be sorted alphabetically on the name of the properties and not on the underlying value.

Finally, when all retrieving and sorting is done, the BugList is returned to the presentation layer where it is displayed in the GridView. Displaying of the Bug objects and paging through the list is all handled by the GridView and works similarly to other GridViews you have seen before. The only thing that might be different is the way that the GridView displays the information for the NameValue objects:

<asp:TemplateField HeaderText=”Feature” SortExpression=”Feature”> <ItemTemplate>

<asp:Label ID=”Label1” runat=”server” Text=’<%# Eval(“Feature.Name”) %>’ />

</ItemTemplate>

<ItemStyle Width=”130px” /> </asp:TemplateField>

The ItemTemplate holds a label with its Text property bound to the Name property of the Feature item so the end user sees the friendly name and not just a number.

With the Reports page done, you have come full circle. Testers and developers can file new bugs in the system. Developers can then change the bugs in the Bug Base, marking them as Closed, Fixed, or Deferred, for example. Members of the Manager group can get detailed lists about bugs in the system on the criteria they specify.

This also concludes the detailed explanation of the pages that make up the Bug Base. The final section of “Code and Code Explanation” lists the other files that are used in the application and describes their purpose.

Other Files and Folders

You have seen many of the concepts used in these files in the previous chapters, so how the files work isn’t explained in detail here. They come with extensive inline documentation where possible, so you’re encouraged to open the files and see how they work.

GridView.skin: This file, located in the BugBase skins folder (under App_Themes), defines the look and feel of the many GridView controls used throughout the site. Instead of defining their looks and behavior in each of the files, a simple skin file was created so you need to define the layout only once. If you want to change any of the colors, CssClasses, PageSize, and padding of the GridView, you should change it right here in the .skin file. The design was abstracted even one step further by not setting fonts and colors in the skin file directly. Instead, various styles, such as the HeaderStyle and AlternatingRowStyle, were defined and their CssClass was set to a class defined in the file Styles.css, discussed later.

Controls: This folder contains a single user control named MemberDetails.ascx that displays information about the current member, and the application she has chosen to work with. This user control is added in the master page, so each page in the site is displaying its content.

Css: To increase the maintainability of the site, almost all presentation details are put in separate CSS files. This allows you to quickly change the look and feel of the site by modifying a few properties in these files. The folder contains four files (outlined in the following table), each serving a distinct purpose:

433

Chapter 12

Filename

Purpose

 

 

Core.css

Contains the behavior for standard HTML elements, such as images and

 

links. It also defines the general layout of the site, such as the location of

 

the menu, the page header, the breadcrumb, and the content section. Refer

 

to the discussion of the master page to see where these classes are used.

Menu.css

Defines the look and feel for the main menu of the application.

PrintStyles.css

The styles in this file are applied when printing pages in the Bug Base.

 

This allows you to hide screen elements that don’t make sense on a

 

printed sheet of paper, such as the main menu.

Styles.css

This CSS file contains all the custom classes used in the site. The selectors

 

in this file change the look and feel of form controls, error messages, and

 

data that is displayed in repeating controls.

 

 

Help: This folder contains the Help index file, accessed by choosing Help Overview from the main menu. This page provides help for the various tasks in the Bug Base. The About page displays general information about the Bug Base.

SiteMap.aspx: Displays a hierarchical view of the site, using a SiteMapDataSource control that in turn uses the file Web.sitemap, located in the root of the site. You can open the SiteMap page by clicking the little Site Map icon on the main menu of the Bug Base.

Images: This folder contains a few images that are used throughout the site, such as the Logo and the background image for the main menu.

JavaScripts: This folder contains a single file called ClientScripts.js that holds various JavaScript functions used at the client.

Maintenance: This folder allows you to make changes to the configuration of the Bug Base. You can add new applications and features; modify the items that appear in the drop-downs for Severity, Reproducibility, and so on; and manage Members. The following table lists each of the pages in the Maintenance folder:

Filename

Purpose

 

 

AddMember.aspx

Allows you to create a new Member. By default, this member will be put

 

in the Tester role.

Applications.aspx

Allows you to create new and change existing applications.

BugProperties.aspx

Allows you to change the items for Severity, Reproducibility, and Frequency.

Default.aspx

This is the homepage for the Maintenance section and provides links to

 

the other pages.

Features.aspx

Allows you to manage the features that belong to an application.

Members.aspx

Displays a list with the Members in the system and allows you to assign

 

members to roles and applications.

Status.aspx

This page allows you to manage the Status items in the system.

 

 

434

The Bug Base

With the discussion of the entire Bug Base application done, including the Management section and all the additional files in the site, it’s time to find out how you can install the Bug Base so you can start using it.

Setting up the Bug Base

Setting up the Bug Base is a pretty straightforward process. You can choose between the installer that comes with this book or manually unzip the application’s files to a folder of your choice. Using the installer is ideal when you have IIS running on your machine and want to use it for the Bug Base. If you plan on using the Bug Base with Visual Web Developer’s web server, the manual deployment is a better choice.

The next two sections describe how to use to the installer and how to manually set up the application. For both methods it’s assumed that the .NET Framework, which is an installation required for Visual Web Developer, has already been installed. It’s also assumed that you have installed SQL Server 2005 Express edition with an instance name of SqlExpress. If you chose a different instance name, make sure you use that name in the set up of the Bug Base.

Using the Installer

On the CD-ROM that comes with this book or from the code download for this chapter that you can get from www.wrox.com, locate the folder Chapter 12 - Bug Base and then open the Installer folder. Inside that folder you’ll find two files: setup.exe and BugBaseInstaller.msi. Double-click setup.exe to start the installation. Keep clicking Next until you get a confirmation dialog that the Bug Base has been installed. Then click Close to dismiss the installer.

On a default installation of Windows, the files that make up the web site are now available in the folder

C:\Inetpub\wwwroot\BugBase.

Before you can browse to the Bug Base, there is one more change to make. By default, if you have earlier versions of the .NET Framework installed, new web sites created in IIS will run against that older version. To tell IIS to use ASP.NET 2.0 instead, you need to change the settings for the virtual folder BugBase so it runs against the .NET 2.0 Framework. Refer to Chapter 5 for detailed instructions about changing these settings.

The Bug Base is now set up to be run under IIS. However, before you can use it there may be a few others settings you need to configure before you can run the Bug Base application. Refer to the section “Browsing to the Bug Base” for the next steps.

Manual Installation

Another way to set up the Bug Base is by manually copying the file from the accompanying zip file to your local hard drive. To install manually, locate the folder Chapter 12 - Bug Base on the CD-ROM or from the code download and then open the Source folder. In that folder you’ll find a zip file called Chapter 12 - Bug Base.zip. Extract the contents of the zip file to a location on your hard drive (for example, C:\Projects). You should end up with a folder similar to C:\Projects\BugBase. If you want to open the web site in Visual Web Developer, choose File Open Web Site and browse to the folder where you extracted the files.

435

Chapter 12

Browsing to the Bug Base

If you used the installer, the Bug Base is now available at http://localhost/BugBase. If you chose the manual installation, you can open the web site in Visual Web Developer and then press F5 to open the site in your browser. The first time the site loads in your browser you may get a time-out error first. The Web.config file for the Bug Base instructs the ASP.NET run time to attach the database for the Bug Base automatically. This happens only the first time you run the application. Attaching the database takes some time, so you could get the time-out error. Whenever that happens, just refresh the browser window and the error will go away.

If your version of SQL Server Express has a different instance name than the default of (local)\SqlExpress, you’ll need to modify the connection string in the Web.config file located in the root of the Bug Base folder. Search the file for (local)\SqlExpress and replace that with the name of your database server and instance name.

The final thing you need to do is make sure that the necessary users are available in the database. The application comes with the five users you have seen before, but you may want to add your own users. To add a user, follow these steps:

1.Start Visual Web Developer and choose File Open Web Site.

2.Browse to the location where you installed the Bug Base. The default location is C:\Inetpub\ wwwroot\BugBase. Click Open. Visual Web Developer opens the project at the specified location. You should see the files that make up the Bug Base listed in the Solution Explorer.

3.Choose Website ASP.NET Configuration. A new browser window starts, showing you the Web Site Administration Tool.

4.Click the Security tab and then click the Create User link. Type in a new username, password, and e-mail address and assign this user to one or more roles.

5.Repeat step 4 for all the users you want to add. When you’ve added all the required users, you can close the Web Site Administration Tool.

Now that you know all about the Bug Base, head to this book’s download page at www.wrox.com to learn about some extensions to the application.

Summar y

This chapter covered a lot of ground in working with ASP.NET 2.0 and Visual Web Developer. The Bug Base application presented in this chapter is the most versatile and extensive application in this entire book. In its current state, it’s ready to be used in a production environment so you and your team members can use it to keep track of the bugs that you find in the applications you built. The introduction of this chapter showed you how to use the Bug Base, including its role-based security features that allow you to make a distinction in functionality between users in the different roles.

In addition to building on the knowledge you gained in previous chapters, this chapter taught you how to do the following:

436