When used alone, its stored values use the generic Object data type, but when inserted into a DataTable object’s Rows collection, all data type limitations and other constraints establi
Trang 156 Microsoft ADO NET 4 Step by Step
3 Locate the CustomerAccounts_RowChanging event handler, which is called whenever
any value in a row changes within the CustomerAccounts table Add the following code,
which checks for valid data involving multiple columns:
If (e.Row.HasVersion(DataRowVersion.Proposed) = True) Then
If (CBool(e.Row("Active", DataRowVersion.Proposed)) = True) And
(IsDBNull(e.Row("StartDate",
DataRowVersion.Proposed)) = True) Then
e.Row.RowError = "Active accounts must have a valid start date."
End If
End If
4 Run the program Use its features to create, edit, and delete data rows When you attempt
to provide invalid data—incorrect data types, violations of business rules, duplicate account names—the program provides the appropriate error messages
Summary
This chapter discussed the DataRow class, the final destination of all data in ADO.NET With one instance created per data record, the DataRow class manages each individual columnar field When used alone, its stored values use the generic Object data type, but when inserted into a DataTable object’s Rows collection, all data type limitations and other constraints
established for the table’s columns act together to verify the row’s content
Beyond these column settings, you can add event handlers to the DataTable that apply custom
business rules to the column and row data, providing an additional layer of validation—and ultimately, integrity—for the table’s data
Trang 2Chapter 3 Storing Data in Memory 57 Chapter 3 Quick Reference
Add a row to a DataTable Use the DataTable object’s NewRow method to obtain a
DataRow instance.
Update the Item values in the DataRow as needed Add the row using the table’s Rows.Add method.
Delete a row from a DataTable Call the DataRow object’s Delete method.
Call the DataTable object’s AcceptChanges method.
Check for data issues in new and modified
DataRow objects
Create a DataTable.
Add DataColumn definitions as needed.
Add event handlers for the DataTable object’s ColumnChanging, RowChanging, or other events.
In the handlers, call the DataRow object’s SetColumnError method or update its RowError property.
Temporarily suspend data validation while
modifying a data row
Call the DataRow object’s BeginEdit method.
Update the Item values in the DataRow as needed.
Call the DataRow object’s EndEdit method.
Trang 459
Chapter 4
Accessing the Right Data Values
After completing this chapter, you will be able to:
■
■ Find items in a DataTable by primary key
■
■ Search for DataRow instances using standard query statements
■
■ Obtain a set of DataRow objects sorted by one or more columns
■
■ Add expression columns to a DataTable that present calculated values
Although adding records to a DataTable is important, the real value of ADO.NET lies in
get-ting those records back out in a variety of ways Fortunately, the data framework includes many different methods and tools to fulfill that very purpose
This chapter introduces a few of the most basic tools, all of which appear in the DataTable, DataColumn, and DataRow classes you’ve already met Each data table includes features that
let you select just the records you need These features are flexible enough to rival those you might find in traditional databases The chapter also includes a discussion of “expression col-umns,” a way to add useful values to each table row without adding any actual data
Note The exercises in this chapter all use the same sample project, a tool that queries data
from a sample DataTable Although you will be able to run the application after each exercise,
the expected results for the full application might not appear until you complete all exercises in the chapter.
Both forms in the sample application use the DataGridView control, one of the standard controls
provided with Visual Studio Chapter 21, “Binding Data with ADO.NET,” discusses the ADO.NET-specific features of this control.
Querying and Sorting Data
In Chapter 3, “Storing Data in Memory,” you learn how to iterate through all the records in a
DataTable object’s Rows collection However, there are times when you need to access only
specific rows, often based on applying search criteria to one or more of the table’s columns Although you can scan through every row in the table, checking each record as you
encoun-ter it to see whether it matches your search limits, the DataTable class already includes fea-tures that will let you select just those DataRow instances that match a selection rule
Trang 5Finding Rows by Primary Key
Each DataTable can include an optional primary key definition, a collection of DataColumn objects assigned to the table’s PrimaryKey member This key is often a unique value from a
single column, but tables also support multicolumn keys After you define a table’s primary key, all rows added to that table must have a non-NULL, unique key
To locate a row based on its primary key, use the table’s Rows.Find method For tables with
single-column keys, pass this method a key value of the appropriate data type For
multi-column keys, pass the key components as an array Find returns a single DataRow instance for
the matching row
C#
// - Single-part key
DataRow matchingRow = someTable.Rows.Find(searchValue);
// - Multi-part key
DataRow matchingRow = someTable.Rows.Find(new Object[]
{keyPart1, keyPart2, keyPart3});
Visual Basic
' - Single-part key
Dim matchingRow As DataRow = someTable.Rows.Find(searchValue)
' - Multi-part key
Dim matchingRow As DataRow = someTable.Rows.Find({keyPart1,
keyPart2, keyPart3})
If no row matches the provided primary key, Find returns Nothing (in Visual Basic) or null (in
C#) The method throws an exception if you apply it to tables with no defined primary key
Note It is possible to add two rows with the same primary key to a DataTable by disabling its constraints (as discussed in Chapter 5, “Bringing Related Data Together”) In such tables, the Find
method returns only the first row with a matching primary key value.
Finding a Row by Primary Key: C#
1 Open the “Chapter 4 CSharp” project from the installed samples folder The project
in-cludes two Windows.Forms classes: TableExaminer and ResultsViewer.
2 Open the source code view for the TableExaminer form Locate the ActPrimaryKey_Click
event handler This routine obtains a long-integer value from the user and then uses it
as the primary key lookup value in the application’s sample DataTable Most of the code
exists to ensure that the user provides a valid ID
Trang 6Chapter 4 Accessing the Right Data Values 61
3 Locate the try catch statement just after the “Perform the lookup” comment In the try
block, add the following statement:
result = workTable.Rows.Find(usePrimaryKey);
This line performs the actual primary-key lookup, returning the DataRow of the match-ing record, or null when the key doesn’t match any of the table’s primary keys.
4 Run the program On the Lookup By Primary Key tab, enter a value in the Primary Key
field (try 2352), and then click the Lookup button The matching row appears in a
sepa-rate window (not shown here)
Finding a Row by Primary Key: Visual Basic
1 Open the “Chapter 4 VB” project from the installed samples folder The project includes
two Windows.Forms classes: TableExaminer and ResultsViewer.
2 Open the source code view for the TableExaminer form Locate the ActPrimaryKey_Click
event handler This routine obtains a long-integer value from the user and then uses it
as the primary key lookup value in the application’s sample DataTable Most of the code
exists to ensure that the user provides a valid ID
3 Locate the Try Catch statement just after the “Perform the lookup” comment In the
Try block, add the following statement:
result = workTable.Rows.Find(usePrimaryKey)
This line performs the actual primary-key lookup, returning the DataRow of the match-ing record, or Nothmatch-ing when the key doesn’t match any of the table’s primary keys.
Trang 762 Microsoft ADO NET 4 Step by Step
4 Run the program On the Lookup By Primary Key tab, enter a value in the Primary Key
field (try 2352), and then click Lookup The matching row appears in a separate window,
as shown here
Selecting Rows with a Search Criteria
The Find method is useful when you need to retrieve a single row based on a primary key
lookup value, but useful data analysis typically involves searching across many of a table’s
columns and returning all possible matches To provide this functionality, the DataTable class includes the Select method.
Note When you anticipate issuing the same Select request on a table multiple times, it’s more efficient to create a DataView that provides a limited presentation of the table’s rows Chapter 6,
“Turning Data into Information,” introduces the DataView class and its use in presenting content
from a data table.
You pass the Select method a string that contains the selection criteria When successful, the method returns an array of matching DataRow instances from the table.
C#
DataRow[] matchingRows = someTable.Select(filterCriteria);
Visual Basic
Dim matchingRows() As DataRow = someTable.Select(filterCriteria)
Note You can iterate through the returned array as your processing needs require Although the rows come to you packaged in an array, they are still part of the original table Any changes you make to these rows affect the underlying table.
Trang 8Chapter 4 Accessing the Right Data Values 63
The filter expression passed to the Select method uses a SQL-like syntax to build a Boolean
statement that will either match or not match specific rows in the table Any of the columns
in your DataTable object is fair game for comparisons As an example, the following expres-sion will return all rows with a Salary column value of at least 100,000:
Salary >= 100000
Columns can be compared to each other and standard mathematical expressions can en-hance the column elements
Bonus > Salary * 0.15
You can string together multiple criteria using the Boolean operators AND, OR, and NOT, and
use parentheses to force evaluation in a specific order
Age >= 18 AND (InSchool = True OR LivingAtHome = True)
Table 4-1 lists the some of the elements you can use in filter expressions To view the full doc-umentation for filter expressions, access the Visual Studio online help entry for “DataColumn Expression Property.”
TABLE 4-1 Filter Expression Elements
Column names Any of the column names from the DataTable Surround column
names that contain embedded space characters or other
non-alphanumeric characters with a set of square brackets, as in [Full Name] for a column named Full Name.
<, >, <=, >=, <>, = Use the standard comparison operators to compare columns to
literal values, to each other, or to more complex expressions.
IN Match from a collection of comma-delimited elements.
BillDenomination IN (5, 10, 20) LIKE Match a string pattern The pattern can include zero or more
oc-currences of the wildcard character (“*” or “%”), but at the ends of the pattern string only, not in the middle.
ProductClass = 'AA*' or:
ProductClass = 'AA%' AND, OR, NOT Use these Boolean operators to join multiple expressions together Parentheses Force the order of expression evaluation with parentheses.
Trang 964 Microsoft ADO NET 4 Step by Step
Literals Literals include integers, decimals, numbers in scientific notation,
strings in single quotes, and dates or times in # marks.
CONVERT Convert an expression or column from one data type to another.
CONVERT(expression, new-type)
The list of allowed data types is pretty close to those allowed when creating data columns There are also restrictions on which data types can be coerced into other types See the Visual Studio online help for full details.
LEN Returns the length of a string column or expression.
ISNULL Returns an expression or a default expression if the first argument
evaluates to NULL Useful for ensuring that a NULL value does not appear in a calculation For example, the following
expres-sion compares the FamilyMembers column to the value 2 when FamilyMembers is not NULL However, if FamilyMembers evaluates
to NULL, it defaults to 1 instead.
ISNULL(FamilyMembers, 1) >= 2 IIF The ternary conditional function, similar to the If and IIf operators
in Visual Basic, and to the :? operator in C# The operator contains
three arguments If the first argument evaluates to true, the
func-tion returns the second argument, the “true” part Otherwise, it returns the third argument, the “false” part.
IIF(Age >= 18, 'Adult', 'Minor') TRIM Trims whitespace from the ends of a string column or expression SUBSTRING Returns a portion of a string column or expression, starting from a
1-based position and continuing on for a specific length count SUBSTRING(PhoneNumber, 1, 3)
Sorting Search Results
By default, the DataTable.Select method returns DataRow objects in the order in which they
were added to the table To sort the results based on one or more columns in the returned
rows, send a second string argument to the Select method that indicates the sort rules.
C#
DataRow[] sortedRows = someTable.Select(filterCriteria, sortRules);
Visual Basic
Dim sortedRows() As DataRow = someTable.Select(filterCriteria, sortRules)
Trang 10Chapter 4 Accessing the Right Data Values 65
The sort string contains a comma-delimited list of the columns to be used for sorting, from
left to right Each column can be optionally followed by ASC for an ascending sort on that column or DESC for a descending sort; ascending is the default The following sort expression
orders the returned rows by descending OrderDate and then by (ascending) customer name:
OrderDate DESC, CustomerName
A third argument to the Select method lets you limit the results based on the state
of each row In tables that have had row-level changes, but for which you haven’t yet
called AcceptResults, this feature can return just the deleted rows, or just the unchanged
rows, among other options See the Visual Studio online help entry “DataViewRowState Enumeration” for a complete list of available options
Selecting and Sorting DataRow Objects: C#
Note This exercise uses the “Chapter 4 CSharp” sample project and continues the previous exer-cise in this chapter.
1 Open the source code view for the TableExaminer form Locate the ActCriteria_Click
event handler This routine collects user-supplied selection and sorting expressions;
then uses them to obtain a set of DataRow instances from a DataTable Most of the
code exists to ensure that the user provides valid expressions
2 Locate the try catch statement just after the “Apply the filter and sorting list” comment
In the try block, add the following statement:
results = workTable.Select(CriteriaFilter.Text, CriteriaSorting.Text);
This line performs the actual row selection, returning the optionally sorted DataRow
instances, or an empty array when the selection expression doesn’t match any of the table’s rows
3 Run the program On the Lookup By Criteria tab, provide expressions that will return
a list of students with improving grades, sorted by name Enter ScoreTrimester3 >
ScoreTrimester1 OR ScoreTrimester3 > ScoreTrimester2 in the Filter Criteria field,
and StudentName in the Sorting List field Click Lookup The matching rows appear in
a separate window