Project Planning: Returning a Single Row
OK, we have three goals so the $64,000 question is... which one first. We need to pull one record, and we need to tell the database which specific record we want. Those two goals then are entangled. The third goal is to take the data and get it into the link factory. The third goal should be relatively simple, and we're not having any trouble testing without it. From a risk management standpoint that's clearly a clean up item that can be worked on later.
Let's take a look at the intertwined problems then. I believe that getting the proper page name into the data query will just be a matter of passing the page as a parameter of a DAL method, and using it to populate a query parameter in the SQL statement. While I haven't created the parameter yet, I have good examples and have worked with databases before. The workload is moderate, but the risk is low. The most potentially difficult portion of the problem then, is most likely getting a single row from the database. From a risk management standpoint then, we should attack 1. returning a single row, 2. passing the query parameter, and 3. passing the data up to the link factory.
From a testing perspective, phase 1 is easy to test. The ObjectDataSource and GridView technique we used on the last page can display output for us to examine. I will need to hard code a SQL WHERE statement to pull a particular row, since I can't yet pass the query parameter. This is easy and let's me attack the hard problem first. Passing the query parameter will be easy to check by performing the same tests, but dropping the hard coded SQL. For phase three, all the input will be handled by the live code and the output tests will simply be to produce the link to spec. And if for any reason I need to tackle phase three before phase two, the hard coded SQL will take care of my input. That even gives me a little flexibility if things need to change. Nevertheless, testing seems feasible in the order dictated by risk management. So let's see if we can pull just one record. Got it!
| PageID |
PageName |
WebsitePathName |
FolderPathName |
| 5 |
VolunteerArticleWriters.aspx |
http://localhost:1204/webroot/EC/ |
Volunteers/ |
As on the other pages, I'm cheating so that you can see the testing results; although the testing statements are cleaned up as I go, I'm hard coding the XHTML output of the tests into the page so that you can follow along. Here's our single row from the hard coded query. I used the same ObjectDataSource and GridView with the same settings except I'm referencing the 06 version of the class, TypeName="BetaTestingDatabase06PagePaths". This may not be the most elegant test, we're returning an entire DataTable, but it confirms that the SQL works. The XHTML output is virtually identical to the ObjectDataSource output from the Database05 page, so I'm not posting it here. Here, however, is the live SQL.
SELECT PagePaths.PageID, PagePaths.PageName, WebsitePaths.WebsitePathName, FolderPaths.FolderPathName FROM PagePaths
INNER JOIN WebsitePaths ON PagePaths.WebsitePathID = WebsitePaths.WebsitePathID
INNER JOIN FolderPaths ON PagePaths.FolderPathID = FolderPaths.FolderPathID
WHERE PagePaths.PageName = 'VolunteerArticleWriters.aspx'
I simply added a WHERE clause to the SQL code. Now I'm calling a specific page, but when generalized, this will be the key part of my solution for using a dynamic query parameter. In his E-Commerce case study, Christian Darie grabs the info by accessing the various fields of a DataRow, so I'm going to attempt to transform my DataTable into a DataRow the same way. Here, Christian transforms the table into a row with this trinary operator.
return (dataContainerForAllPathInformation.Rows.Count > 0) ? dataContainerForAllPathInformation.Rows[0] : null;
Christian's running a safety check to make sure that the results of the search is not an empty set. If the row count is greater than 0, the expression is true and the first item is returned, the first row in the table (the one with index [0]), otherwise the row count is 0 and the second value is returned, null.
Now, I'd also like to address the issue of multiple pages being returned. Unfortunately, examples of live error handling code are too weak to do what I need. I would like to throw a custom exception to warn people that there are two results, so the problem can be fixed. I've simply made some notes and commented them out. As I find more pieces, I can hopefully assemble robust error handling. Ideally, I want to protect against bad data in the database from all directions, validating data being sent to the database from the application, validating data at the database to prevent it from getting in, and handling issues with data coming out of the database. Right now I only have to worry about the output and I've gone as far as I can on that.
Anyway, after my detour into (and frustration with) error handling, I completed testing for the DataRow object returned from the DataTable. Everything looks like it's working OK, though some of the data was not what I expected.
<span class="testOutput">
<%= BetaTestingDatabase06PagePaths.retrieveAllPathInformationFromTheTestingDatabase() %>
</span>
The inline expression returned System.Data.DataRow which is what I figured, however, the ObjectDataSource returned this. Obviously, the GridView isn't behave normally when passed a DataRow object; it wants a DataTable.
Here's the XHTML output, aside from the contents of the table it's pretty similar to what we saw before.
<div class="AspNet-GridView" id="ctl00_ctl00_placeholderForMainBodyContent_placeholderForSection1_GridViewToDisplayPagePathsDataTable">
<table cellpadding="0" cellspacing="0" summary="">
<thead>
<tr>
<th scope="col">HasErrors</th>
<th scope="col">RowError</th>
</tr>
</thead>
<tbody>
<tr>
<td><span disabled="disabled" title="HasErrors"><input id="ctl00_ctl00_placeholderForMainBodyContent_placeholderForSection1_GridViewToDisplayPagePathsDataTable_ctl02_ctl00" type="checkbox" name="ctl00$ctl00$placeholderForMainBodyContent$placeholderForSection1$GridViewToDisplayPagePathsDataTable$ctl02$ctl00" disabled="disabled" /></span></td>
<td> </td>
</tr>
</tbody>
</table>
</div>
This isn't the contents or the field names I expected, but clearly a GridView is set up to handle DataTables and DataSets, not individual DataRows. Nevertheless, it seems clear that the DataRow is being passed back to the page by the method. However, I would still like to see inside that DataRow to make sure. Therefore, I'm going to switch priorities. My most urgent priority is to get the data out of the DataRow and confirm that testing is really complete. I can deal with the dynamic query parameter after that.