制約 (constraint)

テーブルのデータに制限を強制するために、制約を設定できます。

DataTable.Constraintsの戻り値の型はConstraintCollectionであり、その要素の型はConstraintです。よってそこからUniqueConstraintまたはForeignKeyConstraintを得るには、キャストが必要です。

UniqueConstraintクラス

すべての値が唯一でなければならない列の組の、制約を表します。UniqueConstraint Class (System.Data) | Microsoft Learn

プロパティ 内容
DataColumn[] Columns  
bool IsPrimaryKey  
     

DataSet.EnforceConstraintsがfalseに設定されている間は、この制約は強制されません。

DataTable table = new DataTable();

DataColumn column = table.Columns.Add("id");

int c1 = table.Constraints.Count; // 0
column.Unique = true;             // Uniqueを trueとすることで、制約に追加される
int c2 = table.Constraints.Count; // 1


UniqueConstraint constraint = (UniqueConstraint)table.Constraints[0];
string name = constraint.ConstraintName; // "Constraint1"

int length = constraint.Columns.Length; // 1
string columnName = constraint.Columns[0].ColumnName; // "id"

table.Rows.Add(0);
table.Rows.Add(0); // ConstraintException「列 'id' は一意であるように制約されています。値 '0' は既に存在します。

コレクションに制約を追加できるConstraintCollection.Add()、

public System.Data.Constraint Add (
    string name,
    System.Data.DataColumn column,
    bool primaryKey                // columnをPrimaryKeyとするならば、trueを指定する
    );
Add(String, DataColumn, Boolean) - ConstraintCollection.Add Method (System.Data) | Microsoft Learn

を用いれば、追加時にそのインスタンスを取得できます。

DataTable table = new DataTable();

DataColumn column = table.Columns.Add("id");


UniqueConstraint constraint = (UniqueConstraint)table.Constraints.Add("sample", column, false);
string name = constraint.ConstraintName; // "sample"
bool unique = column.Unique; // true

この制約に追加すると、追加した列のUniqueがtrueとなります。

ForeignKeyConstraintクラス

プロパティ 内容
string ConstraintName ConstraintCollection内での、制約の名前
Rule DeleteRule 親テーブルから行が削除されたときの、子テーブルの動作
Rule UpdateRule 親テーブルの行が更新されたときの、子テーブルの動作
AcceptRejectRule AcceptRejectRule 親テーブルでAcceptChanges()が呼ばれたときの、子テーブルの動作
     
Properties - ForeignKeyConstraint Class (System.Data) | Microsoft Learn

この制約を指定した行が、親行の影響を受けるようになります。つまり制約は、子テーブルの側に指定します。

親テーブルの行と関連のない子テーブルの行は、制約の影響を受けません。これには子テーブルのNewRow()で作成した、子テーブルに追加する前の行などが該当します。

Rule 列挙型
列挙子 数値 内容
None 0 関連する行に、何もしない
Cascade 1 関連する行を、削除または更新する [既定]

Relationに指定されている列が更新されると、その子行の列もその値に更新される ※1

SetDefault 3 関連する行の値を、DataColumn.DefaultValueの値にする
SetNull 2 関連する行の値を、DBNullにする
Rule Enum (System.Data) | Microsoft Learn

※1 Relationに指定されている列があるにもかかわらずDeleteRuleがCascadeに設定されていないと、その列の値を変更したときに「制約がリレーションシップ Relation で適用され、この値を変更すると子行が孤立するため、変更できません。」としてInvalidConstraintExceptionが投げられます。

AcceptRejectRule

AcceptChanges()が呼ばれたときの制約を表します。ForeignKeyConstraint.AcceptRejectRule Property (System.Data) | Microsoft Learn

このAcceptChanges()はDataTable.LoadDataRow()やDataRowCollection.Remove()の内部からも呼ばれるため、この制約はこれらのメソッドにも作用します。AcceptChanges - AcceptChanges

ドキュメントにはありませんが、プロパティ名にあるようにRejectChanges()にも影響します。

AcceptRejectRule 列挙型
列挙子 数値 内容
None 0 No action occurs [既定]
Cascade 1 Changes are cascaded across the relationship.
AcceptRejectRule Enum (System.Data) | Microsoft Learn
DataSet dataSet = new DataSet();
DataTable tableA = dataSet.Tables.Add();
DataTable tableB = dataSet.Tables.Add();

DataColumn columnA = tableA.Columns.Add();
DataColumn columnB = tableB.Columns.Add();

DataRelation relation = dataSet.Relations.Add(columnA, columnB);

DataRow rowA = tableA.Rows.Add("1");
DataRow rowB = tableB.Rows.Add("1");


ForeignKeyConstraint constraint = relation.ChildKeyConstraint;
string name = constraint.ConstraintName; // "Relation1"

Rule rule1 = constraint.DeleteRule; // Cascade
Rule rule2 = constraint.UpdateRule; // Cascade

AcceptRejectRule rule1 = constraint.AcceptRejectRule; // None

このAcceptRejectRuleがNoneの状態ではAcceptChanges()は子テーブルに作用しませんが、Cascadeとすると作用するようになります。

DataRowState stateA1 = rowA.RowState; // Added 
DataRowState stateB1 = rowB.RowState; // Added

rowA.AcceptChanges();
DataRowState stateA2 = rowA.RowState; // Unchanged 
DataRowState stateB2 = rowB.RowState; // Added. 子テーブルには作用しない
rowA.SetAdded(); // rowA.RowStateを Addedに戻す

constraint.AcceptRejectRule = AcceptRejectRule.Cascade;

rowA.AcceptChanges();
DataRowState stateA3 = rowA.RowState; // Unchanged 
DataRowState stateB3 = rowB.RowState; // Unchanged. 子テーブルにも作用している
Microsoft Learnから検索