Adding Restrictions to DataTable and DataColumn Objects
As you know, a DataSet object is used to store a copy of a subset of the database. For
example, you can store a copy of the rows from database tables into a DataSet, with each
table represented by a DataTable object. A DataTable stores columns in DataColumn
objects.
In addition to storing rows retrieved from a database table, you can also add restrictions
to a DataTable and its DataColumn objects. This allows you to model the same
restrictions placed on the database tables and columns in your DataTable and
DataColumn objects. For example, you can add the following constraints to a DataTable:
•
Unique
•
Primary key
•
Foreign key
In addition, you can add the following restrictions to a DataColumn:
•
Whether the column can accept a null value-which you store in the AllowDBNull
property of the DataColumn.
•
Any auto-increment information-which you store in the AutoIncrement,
AutoIncrementSeed, and AutoIncrementStep properties of the DataColumn. You
set these properties when adding rows to a DataTable with a corresponding
database table that contains an identity column. The ProductID column of the
Products table is an example of an identity column.
Note ADO.NET will not automatically generate values for identity columns in a
new row. Only the database can do that. You must read the generated
identity value for the column from the database. You'll see how to do that
later in the sections "Retrieving New Identity Column Values
" and "Using
Stored Procedures to Add, Modify, and Remove Rows from the Database."
Also, if your database table contains columns that are assigned a default
value, you should read that value from the database. This is better than
setting the DefaultValue property of a DataColumn because if the default
value set in the database table definition changes, you can pick up the new
value from the database rather than having to change your code.
•
The maximum length of a string or character column value-which you store in the
MaxLength property of the DataColumn.
•
Whether the column is read-only-which you store in the ReadOnly property of the
DataColumn.
•
Whether the column is unique-which you store in the Unique property of the
DataColumn.
By adding these restrictions up front, you prevent bad data from being added to your
DataSet to begin with. This helps reduce the errors when attempting to push changes in
your DataSet to the database. If a user of your program attempts to add data that violates
a restriction, they'll cause an exception to be thrown. You can then catch the exception in
your program and display a message with the details. The user can then change the data
they were trying to add and fix the problem.
You also need to define a primary key before you can find, filter, and sort DataRow
objects in a DataTable. You'll learn how to do that later in the section "Finding, Filtering,
and Sorting Rows in a DataTable."
Tip Adding constraints causes a performance degradation when you call the Fill()
method of a DataAdapter. This is because the retrieved rows are checked against
your constraints before they are added to your DataSet. You should therefore set the
EnforceConstraints property of your DataSet to false before calling the Fill()
method. You then set EnforceConstraints back to the default of true after the call to
Fill().
You can use one of following ways to add restrictions to DataTable and DataColumn
objects:
•
Add the restrictions yourself by setting the properties of your DataTable and
DataColumn objects. This results in the fastest executing code.
•
Call the FillSchema() method of your DataAdapter to copy the schema
information from the database to your DataSet. This populates the properties of
the DataTable objects and their DataColumn objects automatically. Although
simple to call, the FillSchema() method takes a relatively long time to read the
schema information from the database and you should avoid using it.
You'll learn the details of both these techniques in the following sections.
Adding the Restrictions Yourself
You can add restrictions to your DataTable and DataColumn objects yourself using the
properties of the DataTable and DataColumn objects.
For example, assume you have a DataSet object named myDataSet that contains three
DataTable objects named Products, Orders, and Order Details that have been populated
using the following code:
SqlCommand mySqlCommand = mySqlConnection.CreateCommand();
mySqlCommand.CommandText =
"SELECT ProductID, ProductName " +
"FROM Products;" +
"SELECT OrderID " +
"FROM Orders;" +
"SELECT OrderID, ProductID, UnitPrice " +
"FROM [Order Details];";
SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter();
mySqlDataAdapter.SelectCommand = mySqlCommand;
DataSet myDataSet = new DataSet();
mySqlConnection.Open();
mySqlDataAdapter.Fill(myDataSet);
mySqlConnection.Close();
myDataSet.Tables["Table"].TableName = "Products";
myDataSet.Tables["Table1"].TableName = "Orders";
myDataSet.Tables["Table2"].TableName = "Order Details";
The primary key for the Products table is the ProductID column; the primary key for the
Orders table is the OrderID column; and the primary key for the Order Details table is
made up of both the OrderID and ProductID columns.
Note You must include all the columns of a database table's primary key in your query if
you want to define a primary key on those columns in your DataTable.
In the following sections, you'll see how to
•
Add constraints to the Products, Orders, and Order Details DataTable objects.
•
Restrict the values placed in the DataColumn objects of the Products DataTable.
Adding Constraints to DataTable Objects
In this section, you'll see how to add constraints to DataTable objects. Specifically, you'll
see how to add primary key constraints to the Products, Orders, and Order Details
DataTable objects. A primary key constraint is actually implemented as a unique
constraint. You'll also see how to add foreign key constraints from the Order Details to
the Products and Orders DataTable objects.
Constraints are stored in a ConstraintCollection object that stores Constraint objects. You
access the ConstraintCollection using the DataTable object's Constraints property. To add
a new Constraint object to ConstraintCollection, you call the Add() method through the
Constraints property. The Add() method allows you to add unique constraints and foreign
key constraints to a DataTable. Since a primary key constraint is implemented as a
unique constraint, you can also use the Add() method to add a primary constraint to a
DataTable. You'll see how to use the Add() method shortly.
You can also add a primary key constraint to a DataTable object by setting its
PrimaryKey property, which you set to an array of DataColumn objects that make up the
primary key. An array is required because the primary key of a database table can be
made up of multiple columns. As you'll see in the examples, this is simpler than using the
Add() method to add a primary key constraint.
CALLING THE Fill() METHOD OF A DataAdapter MORE THAN ONCE
The Fill() method retrieves all of the rows from the database table, as specified in your
DataAdapter object's SelectCommand property. If you add a primary key to your
DataTable, then calling the Fill() method more than once will put the retrieved rows in
your DataTable and throw away any existing rows with matching primary key column
values already in your DataTable.
If you don't add a primary key to your DataTable, then calling the Fill() method more
than once will simply add all the retrieved rows to your DataTable again, duplicating the
rows already there.
This is another reason for adding a primary key constraint to your DataTable because you
don't want duplicate rows.
Adding a Primary Key to the Products DataTable
Let's take a look at adding a primary key to the Products DataTable. First, the following
example creates a DataTable object named productsDataTable and sets it to the Products
DataTable retrieved from myDataSet:
DataTable productsDataTable = myDataSet.Tables["Products"];
Now, the primary key for the Products database table is the ProductID column; therefore,
you need to set the PrimaryKey property of productsDataTable to an array containing the
ProductID DataColumn object. The following example shows how you do this. It creates
an array of DataColumn objects named productsPrimaryKey and initializes it to the
ProductID column of productsDataTable, then sets the PrimaryKey property of
productsDataTable to the array:
DataColumn[] productsPrimaryKey =
new DataColumn[]
{
productsDataTable.Columns["ProductID"]
};
productsDataTable.PrimaryKey = productsPrimaryKey;
When you set the PrimaryKey property of a DataTable, the AllowDBNull and Unique
properties of the DataColumn object are automatically changed as follows:
•
The AllowDBNull property is changed to false and indicates that the DataColumn
cannot accept a null value.
•
The Unique property is changed to true and indicates that the DataColumn value
in each DataRow must be unique.
In the previous example, therefore, the AllowDBNull and Unique properties of the
ProductID DataColumn are automatically changed to false and true, respectively.
Adding a Primary Key to the Orders DataTable
The following example sets the PrimaryKey property of the Orders DataTable to the
OrderID DataColumn:
myDataSet.Tables["Orders"].PrimaryKey =
new DataColumn[]
{
myDataSet.Tables["Orders"].Columns["OrderID"]
};
Notice I've used just one statement in this example to make it more concise than the
previous example.
You can also use the Add() method to add a unique, primary key, or foreign key
constraint to a DataTable. The Add() method is overloaded as follows:
void Add(Constraint myConstraint) // adds any constraint
void Add(string constraintName, DataColumn myDataColumn,
bool isPrimaryKey) // adds a primary key or unique constraint
void Add(string constraintName, DataColumn parentColumn,
DataColumn childColumn) // adds a foreign key constraint
void Add(string constraintName, DataColumn[] myDataColumn,
bool isPrimaryKey) // adds a primary key or unique constraint
void Add(string cosntraintName, DataColumn[] parentColumns,
DataColumn[] childColumns) // adds a foreign key constraint
where