Every DataSet
contains a
DataRelationCollection
object, which contains
DataRelation
objects. Each
DataRelation
defines a relationship between two
tables. There are two
reasons why you might define
DataRelation
objects:
To provide better error checking (for example, spotting an orphaned
child before you reconnect to update the data source). This
functionality is provided through the
ForeignKeyConstraint
, which the
DataRelation
can create implicitly.
To provide better navigation.
A typical DataRelation
requires three pieces of
information: a descriptive name of your choosing (which
doesn’t signify anything or relate to the data
source), a reference to the parent DataColumn
, and
a reference to the child DataColumn
. You can add
this information with the DataRelation
constructor:
DataRelation relation = new DataRelation("Name", ParentCol, ChildCol);
Once you’ve defined the
DataRelation
object, you need to add it to the
DataSet
:
ds.Relations.Add(relation);
For example, to create a relationship between product categories and products, use the following code:
DataColumn parentCol = ds.Tables["Categories"].Columns["CategoryID"]; DataColumn childCol = ds.Tables["Products"].Columns["CategoryID"]; DataRelation relation = new DataRelation("Cat_Prod", parentCol, childCol); ds.Relations.Add(relation);
If you attempt to create a DataRelation
, and the
parent record column isn’t unique, or there are
child records that refer to nonexistent parents, an exception is
thrown.
Currently, there is no way to automatically add a
DataRelation
object based on the relationships
defined in the data source. That means that if you want to use
relations, either to enforce data integrity or provide parent-child
navigation, you need to create them yourself.
At first glance, the
DataRelation
seems to duplicate some of the
functionality provided by the ForeignKeyConstraint
to enforce referential integrity. However, this
isn’t really the case. The
DataRelation
actually uses the
Constraint
classes. By default, when you create a
new DataRelation
, a
UniqueConstraint
is created automatically for the
parent column and added to the parent table if it
doesn’t already exist. At the same time, a
ForeignKeyConstraint
is created for the child
column and added to the child table. Thus, the rules governing
relational integrity are exactly the same as those discussed in Chapter 10.
In some cases, you might want to create a
DataRelation
without generating any constraints.
This allows you to use a relation to aid navigation but not enforce
it as a data validation rule. One reason why you might not want to
enforce a relationship could be because you have queried only a
subset of the total parent rows in the data source. Consequently,
there may be child rows that reference a valid parent row that
isn’t a part of the DataSet
.
To make sure constraints are generated, you must use the
DataRelation
constructor that accepts an
additional Boolean value. If you supply false
, the
Constraint
objects aren’t
generated.
DataRelation relation = new DataRelation("Name", ParentCol, ChildCol, false);
You can still use the DataRelation
for navigation,
but referentially integrity will not be enforced on the
DataSet
.
As explained in Chapter 10, you can configure the
ForeignKeyConstraint
to specify if deletes and updates are cascaded. When using a
DataRelation
, you can still use this feature; you
simply need to modify the automatically generated
ForeignKeyConstraint
, which is available through
the DataRelation.ChildKeyConstraint
property.