| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- //------------------------------------------------------------------------------
- // <copyright file="ForeignKeyConstraint.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- // <owner current="false" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data {
- using System;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Data.Common;
- /// <devdoc>
- /// <para>Represents an action
- /// restriction enforced on a set of columns in a primary key/foreign key relationship when
- /// a value or row is either deleted or updated.</para>
- /// </devdoc>
- [
- DefaultProperty("ConstraintName"),
- Editor("Microsoft.VSDesigner.Data.Design.ForeignKeyConstraintEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
- ]
- public class ForeignKeyConstraint : Constraint {
- // constants
- internal const Rule Rule_Default = Rule.Cascade;
- internal const AcceptRejectRule AcceptRejectRule_Default = AcceptRejectRule.None;
- // properties
- internal Rule deleteRule = Rule_Default;
- internal Rule updateRule = Rule_Default;
- internal AcceptRejectRule acceptRejectRule = AcceptRejectRule_Default;
- private DataKey childKey;
- private DataKey parentKey;
- // Design time serialization
- internal string constraintName = null;
- internal string[] parentColumnNames = null;
- internal string[] childColumnNames = null;
- internal string parentTableName = null;
- internal string parentTableNamespace = null;
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified parent and
- /// child <see cref='System.Data.DataColumn'/> objects.
- /// </para>
- /// </devdoc>
- public ForeignKeyConstraint(DataColumn parentColumn, DataColumn childColumn)
- : this(null, parentColumn, childColumn) {
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified name,
- /// parent and child <see cref='System.Data.DataColumn'/> objects.
- /// </para>
- /// </devdoc>
- public ForeignKeyConstraint(string constraintName, DataColumn parentColumn, DataColumn childColumn) {
- DataColumn[] parentColumns = new DataColumn[] {parentColumn};
- DataColumn[] childColumns = new DataColumn[] {childColumn};
- Create(constraintName, parentColumns, childColumns);
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified arrays
- /// of parent and child <see cref='System.Data.DataColumn'/> objects.
- /// </para>
- /// </devdoc>
- public ForeignKeyConstraint(DataColumn[] parentColumns, DataColumn[] childColumns)
- : this(null, parentColumns, childColumns) {
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified name,
- /// and arrays of parent and child <see cref='System.Data.DataColumn'/> objects.
- /// </para>
- /// </devdoc>
- public ForeignKeyConstraint(string constraintName, DataColumn[] parentColumns, DataColumn[] childColumns) {
- Create(constraintName, parentColumns, childColumns);
- }
- // construct design time object
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- [Browsable(false)]
- public ForeignKeyConstraint(string constraintName, string parentTableName, string[] parentColumnNames, string[] childColumnNames,
- AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule) {
- this.constraintName = constraintName;
- this.parentColumnNames = parentColumnNames;
- this.childColumnNames = childColumnNames;
- this.parentTableName = parentTableName;
- this.acceptRejectRule = acceptRejectRule;
- this.deleteRule = deleteRule;
- this.updateRule = updateRule;
- // ForeignKeyConstraint(constraintName, parentTableName, null, parentColumnNames, childColumnNames,acceptRejectRule, deleteRule, updateRule)
- }
- // construct design time object
- [Browsable(false)]
- public ForeignKeyConstraint(string constraintName, string parentTableName, string parentTableNamespace, string[] parentColumnNames,
- string[] childColumnNames, AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule) {
- this.constraintName = constraintName;
- this.parentColumnNames = parentColumnNames;
- this.childColumnNames = childColumnNames;
- this.parentTableName = parentTableName;
- this.parentTableNamespace= parentTableNamespace;
- this.acceptRejectRule = acceptRejectRule;
- this.deleteRule = deleteRule;
- this.updateRule = updateRule;
- }
- /// <devdoc>
- /// The internal constraint object for the child table.
- /// </devdoc>
- internal DataKey ChildKey {
- get {
- CheckStateForProperty();
- return childKey;
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the child columns of this constraint.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.ForeignKeyConstraintChildColumnsDescr),
- ReadOnly(true)
- ]
- public virtual DataColumn[] Columns {
- get {
- CheckStateForProperty();
- return childKey.ToArray();
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the child table of this constraint.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.ConstraintTableDescr),
- ReadOnly(true)
- ]
- public override DataTable Table {
- get {
- CheckStateForProperty();
- return childKey.Table;
- }
- }
- internal string[] ParentColumnNames {
- get {
- return parentKey.GetColumnNames();
- }
- }
- internal string[] ChildColumnNames {
- get {
- return childKey.GetColumnNames();
- }
- }
- internal override void CheckCanAddToCollection(ConstraintCollection constraints) {
- if (Table != constraints.Table)
- throw ExceptionBuilder.ConstraintAddFailed(constraints.Table);
- if (Table.Locale.LCID != RelatedTable.Locale.LCID || Table.CaseSensitive != RelatedTable.CaseSensitive)
- throw ExceptionBuilder.CaseLocaleMismatch();
- }
- internal override bool CanBeRemovedFromCollection(ConstraintCollection constraints, bool fThrowException) {
- return true;
- }
- internal bool IsKeyNull( object[] values ) {
- for (int i = 0; i < values.Length; i++) {
- if (! DataStorage.IsObjectNull(values[i]))
- return false;
- }
- return true;
- }
- internal override bool IsConstraintViolated() {
- Index childIndex = childKey.GetSortIndex();
- object[] uniqueChildKeys = childIndex.GetUniqueKeyValues();
- bool errors = false;
- Index parentIndex = parentKey.GetSortIndex();
- for (int i = 0; i < uniqueChildKeys.Length; i++) {
- object[] childValues = (object[]) uniqueChildKeys[i];
- if (!IsKeyNull(childValues)) {
- if (!parentIndex.IsKeyInIndex(childValues)) {
- DataRow[] rows = childIndex.GetRows(childIndex.FindRecords(childValues));
- string error = Res.GetString(Res.DataConstraint_ForeignKeyViolation, ConstraintName, ExceptionBuilder.KeysToString(childValues));
- for (int j = 0; j < rows.Length; j++) {
- rows[j].RowError = error;
- }
- errors = true;
- }
- }
- }
- return errors;
- }
- internal override bool CanEnableConstraint() {
- if (Table.DataSet == null || !Table.DataSet.EnforceConstraints)
- return true;
- Index childIndex = childKey.GetSortIndex();
- object[] uniqueChildKeys = childIndex.GetUniqueKeyValues();
- Index parentIndex = parentKey.GetSortIndex();
- for (int i = 0; i < uniqueChildKeys.Length; i++) {
- object[] childValues = (object[]) uniqueChildKeys[i];
- if (!IsKeyNull(childValues) && !parentIndex.IsKeyInIndex(childValues)) {
- return false;
- }
- }
- return true;
- }
- internal void CascadeCommit(DataRow row) {
- if (row.RowState == DataRowState.Detached)
- return;
- if (acceptRejectRule == AcceptRejectRule.Cascade) {
- Index childIndex = childKey.GetSortIndex( row.RowState == DataRowState.Deleted ? DataViewRowState.Deleted : DataViewRowState.CurrentRows );
- object[] key = row.GetKeyValues(parentKey, row.RowState == DataRowState.Deleted ? DataRowVersion.Original : DataRowVersion.Default );
- if (IsKeyNull(key)) {
- return;
- }
- Range range = childIndex.FindRecords(key);
- if (!range.IsNull) {
- // SQLBU 499726 - DataTable internal index is corrupted: '13'
- // Self-referencing table has suspendIndexEvents, in the multi-table scenario the child table hasn't
- // this allows the self-ref table to maintain the index while in the child-table doesn't
- DataRow[] rows = childIndex.GetRows(range);
- foreach(DataRow childRow in rows) {
- if (DataRowState.Detached != childRow.RowState) {
- if (childRow.inCascade)
- continue;
- childRow.AcceptChanges();
- }
- }
- }
- }
- }
- internal void CascadeDelete(DataRow row) {
- if (-1 == row.newRecord)
- return;
- object[] currentKey = row.GetKeyValues(parentKey, DataRowVersion.Current);
- if (IsKeyNull(currentKey)) {
- return;
- }
- Index childIndex = childKey.GetSortIndex();
- switch (DeleteRule) {
- case Rule.None: {
- if (row.Table.DataSet.EnforceConstraints) {
- // if we're not cascading deletes, we should throw if we're going to strand a child row under enforceConstraints.
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- if (range.Count == 1 && childIndex.GetRow(range.Min) == row)
- return;
- throw ExceptionBuilder.FailedCascadeDelete(ConstraintName);
- }
- }
- break;
- }
- case Rule.Cascade: {
- object[] key = row.GetKeyValues(parentKey, DataRowVersion.Default);
- Range range = childIndex.FindRecords(key);
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- DataRow r = rows[j];
- if (r.inCascade)
- continue;
- r.Table.DeleteRow(r);
- }
- }
- break;
- }
- case Rule.SetNull: {
- object[] proposedKey = new object[childKey.ColumnsReference.Length];
- for (int i = 0; i < childKey.ColumnsReference.Length; i++)
- proposedKey[i] = DBNull.Value;
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- // if (rows[j].inCascade)
- // continue;
- if (row != rows[j])
- rows[j].SetKeyValues(childKey, proposedKey);
- }
- }
- break;
- }
- case Rule.SetDefault: {
- object[] proposedKey = new object[childKey.ColumnsReference.Length];
- for (int i = 0; i < childKey.ColumnsReference.Length; i++)
- proposedKey[i] = childKey.ColumnsReference[i].DefaultValue;
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- // if (rows[j].inCascade)
- // continue;
- if (row != rows[j])
- rows[j].SetKeyValues(childKey, proposedKey);
- }
- }
- break;
- }
- default: {
- Debug.Assert(false, "Unknown Rule value");
- break;
- }
- }
- }
- internal void CascadeRollback(DataRow row) {
- Index childIndex = childKey.GetSortIndex( row.RowState == DataRowState.Deleted ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows);
- object[] key = row.GetKeyValues(parentKey, row.RowState == DataRowState.Modified ? DataRowVersion.Current : DataRowVersion.Default );
- // Bug : This is definitely not a proper fix. (Ref. MDAC Bug 73592)
- if (IsKeyNull(key)) {
- return;
- }
- Range range = childIndex.FindRecords(key);
- if (acceptRejectRule == AcceptRejectRule.Cascade) {
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- if (rows[j].inCascade)
- continue;
- rows[j].RejectChanges();
- }
- }
- }
- else {
- // AcceptRejectRule.None
- if (row.RowState != DataRowState.Deleted && row.Table.DataSet.EnforceConstraints) {
- if (!range.IsNull) {
- if (range.Count == 1 && childIndex.GetRow(range.Min) == row)
- return;
- if (row.HasKeyChanged(parentKey)) {// if key is not changed, this will not cause child to be stranded
- throw ExceptionBuilder.FailedCascadeUpdate(ConstraintName);
- }
- }
- }
- }
- }
- internal void CascadeUpdate(DataRow row) {
- if (-1 == row.newRecord)
- return;
- object[] currentKey = row.GetKeyValues(parentKey, DataRowVersion.Current);
- if (!Table.DataSet.fInReadXml && IsKeyNull(currentKey)) {
- return;
- }
- Index childIndex = childKey.GetSortIndex();
- switch (UpdateRule) {
- case Rule.None: {
- if (row.Table.DataSet.EnforceConstraints)
- {
- // if we're not cascading deletes, we should throw if we're going to strand a child row under enforceConstraints.
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- throw ExceptionBuilder.FailedCascadeUpdate(ConstraintName);
- }
- }
- break;
- }
- case Rule.Cascade: {
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- object[] proposedKey = row.GetKeyValues(parentKey, DataRowVersion.Proposed);
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- // if (rows[j].inCascade)
- // continue;
- rows[j].SetKeyValues(childKey, proposedKey);
- }
- }
- break;
- }
- case Rule.SetNull: {
- object[] proposedKey = new object[childKey.ColumnsReference.Length];
- for (int i = 0; i < childKey.ColumnsReference.Length; i++)
- proposedKey[i] = DBNull.Value;
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- // if (rows[j].inCascade)
- // continue;
- rows[j].SetKeyValues(childKey, proposedKey);
- }
- }
- break;
- }
- case Rule.SetDefault: {
- object[] proposedKey = new object[childKey.ColumnsReference.Length];
- for (int i = 0; i < childKey.ColumnsReference.Length; i++)
- proposedKey[i] = childKey.ColumnsReference[i].DefaultValue;
- Range range = childIndex.FindRecords(currentKey);
- if (!range.IsNull) {
- DataRow[] rows = childIndex.GetRows(range);
- for (int j = 0; j < rows.Length; j++) {
- // if (rows[j].inCascade)
- // continue;
- rows[j].SetKeyValues(childKey, proposedKey);
- }
- }
- break;
- }
- default: {
- Debug.Assert(false, "Unknown Rule value");
- break;
- }
- }
- }
- internal void CheckCanClearParentTable(DataTable table) {
- if (Table.DataSet.EnforceConstraints && Table.Rows.Count > 0) {
- throw ExceptionBuilder.FailedClearParentTable(table.TableName, ConstraintName, Table.TableName);
- }
- }
- internal void CheckCanRemoveParentRow(DataRow row) {
- Debug.Assert(Table.DataSet != null, "Relation " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening.");
- if (!Table.DataSet.EnforceConstraints)
- return;
- if (DataRelation.GetChildRows(this.ParentKey, this.ChildKey, row, DataRowVersion.Default).Length > 0) {
- throw ExceptionBuilder.RemoveParentRow(this);
- }
- }
- internal void CheckCascade(DataRow row, DataRowAction action) {
- Debug.Assert(Table.DataSet != null, "ForeignKeyConstraint " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening.");
- if (row.inCascade)
- return;
- row.inCascade = true;
- try {
- if (action == DataRowAction.Change) {
- if (row.HasKeyChanged(parentKey)) {
- CascadeUpdate(row);
- }
- }
- else if (action == DataRowAction.Delete) {
- CascadeDelete(row);
- }
- else if (action == DataRowAction.Commit) {
- CascadeCommit(row);
- }
- else if (action == DataRowAction.Rollback) {
- CascadeRollback(row);
- }
- else if (action == DataRowAction.Add) {
- }
- else {
- Debug.Assert(false, "attempt to cascade unknown action: " + ((Enum) action).ToString());
- }
- }
- finally {
- row.inCascade = false;
- }
- }
- internal override void CheckConstraint(DataRow childRow, DataRowAction action) {
- if ((action == DataRowAction.Change ||
- action == DataRowAction.Add ||
- action == DataRowAction.Rollback) &&
- Table.DataSet != null && Table.DataSet.EnforceConstraints &&
- childRow.HasKeyChanged(childKey)) {
- // This branch is for cascading case verification.
- DataRowVersion version = (action == DataRowAction.Rollback) ? DataRowVersion.Original : DataRowVersion.Current;
- object[] childKeyValues = childRow.GetKeyValues(childKey);
- // check to see if this is just a change to my parent's proposed value.
- if (childRow.HasVersion(version)) {
- // this is the new proposed value for the parent.
- DataRow parentRow = DataRelation.GetParentRow(this.ParentKey, this.ChildKey, childRow, version);
- if(parentRow != null && parentRow.inCascade) {
- object[] parentKeyValues = parentRow.GetKeyValues(parentKey, action == DataRowAction.Rollback ? version : DataRowVersion.Default);
- int parentKeyValuesRecord = childRow.Table.NewRecord();
- childRow.Table.SetKeyValues(childKey, parentKeyValues, parentKeyValuesRecord);
- if (childKey.RecordsEqual(childRow.tempRecord, parentKeyValuesRecord)) {
- return;
- }
- }
- }
- // now check to see if someone exists... it will have to be in a parent row's current, not a proposed.
- object[] childValues = childRow.GetKeyValues(childKey);
- if (!IsKeyNull(childValues)) {
- Index parentIndex = parentKey.GetSortIndex();
- if (!parentIndex.IsKeyInIndex(childValues)) {
- // could be self-join constraint
- if (childKey.Table == parentKey.Table && childRow.tempRecord != -1) {
- int lo = 0;
- for (lo = 0; lo < childValues.Length; lo++) {
- DataColumn column = parentKey.ColumnsReference[lo];
- object value = column.ConvertValue(childValues[lo]);
- if (0 != column.CompareValueTo(childRow.tempRecord, value)) {
- break;
- }
- }
- if (lo == childValues.Length) {
- return;
- }
- }
- throw ExceptionBuilder.ForeignKeyViolation(ConstraintName, childKeyValues);
- }
- }
- }
- }
- private void NonVirtualCheckState () {
- if (_DataSet == null) {
- // Make sure columns arrays are valid
- parentKey.CheckState();
- childKey.CheckState();
- if (parentKey.Table.DataSet != childKey.Table.DataSet) {
- throw ExceptionBuilder.TablesInDifferentSets();
- }
- for (int i = 0; i < parentKey.ColumnsReference.Length; i++) {
- if (parentKey.ColumnsReference[i].DataType != childKey.ColumnsReference[i].DataType ||
- ((parentKey.ColumnsReference[i].DataType == typeof(DateTime)) && (parentKey.ColumnsReference[i].DateTimeMode != childKey.ColumnsReference[i].DateTimeMode) && ((parentKey.ColumnsReference[i].DateTimeMode & childKey.ColumnsReference[i].DateTimeMode) != DataSetDateTime.Unspecified)))
- throw ExceptionBuilder.ColumnsTypeMismatch();
- }
- if (childKey.ColumnsEqual(parentKey)) {
- throw ExceptionBuilder.KeyColumnsIdentical();
- }
- }
- }
- // If we're not in a DataSet relations collection, we need to verify on every property get that we're
- // still a good relation object.
- internal override void CheckState() {
- NonVirtualCheckState ();
- }
- /// <devdoc>
- /// <para>
- /// Indicates what kind of action should take place across
- /// this constraint when <see cref='System.Data.DataTable.AcceptChanges'/>
- /// is invoked.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- DefaultValue(AcceptRejectRule_Default),
- ResDescriptionAttribute(Res.ForeignKeyConstraintAcceptRejectRuleDescr)
- ]
- public virtual AcceptRejectRule AcceptRejectRule {
- get {
- CheckStateForProperty();
- return acceptRejectRule;
- }
- set {
- switch(value) { // @perfnote: Enum.IsDefined
- case AcceptRejectRule.None:
- case AcceptRejectRule.Cascade:
- this.acceptRejectRule = value;
- break;
- default:
- throw Common.ADP.InvalidAcceptRejectRule(value);
- }
- }
- }
- internal override bool ContainsColumn(DataColumn column) {
- return parentKey.ContainsColumn(column) || childKey.ContainsColumn(column);
- }
- internal override Constraint Clone(DataSet destination) {
- return Clone(destination, false);
- }
-
- internal override Constraint Clone(DataSet destination, bool ignorNSforTableLookup) {
- int iDest;
- if (ignorNSforTableLookup) {
- iDest = destination.Tables.IndexOf(Table.TableName);
- }
- else {
- iDest = destination.Tables.IndexOf(Table.TableName, Table.Namespace, false); // pass false for last param
- // to be backward compatable, otherwise meay cause new exception
- }
-
- if (iDest < 0)
- return null;
- DataTable table = destination.Tables[iDest];
- if (ignorNSforTableLookup) {
- iDest = destination.Tables.IndexOf(RelatedTable.TableName);
- }
- else {
- iDest = destination.Tables.IndexOf(RelatedTable.TableName, RelatedTable.Namespace, false);// pass false for last param
- }
- if (iDest < 0)
- return null;
- DataTable relatedTable = destination.Tables[iDest];
- int keys = Columns.Length;
- DataColumn[] columns = new DataColumn[keys];
- DataColumn[] relatedColumns = new DataColumn[keys];
- for (int i = 0; i < keys; i++) {
- DataColumn src = Columns[i];
- iDest = table.Columns.IndexOf(src.ColumnName);
- if (iDest < 0)
- return null;
- columns[i] = table.Columns[iDest];
- src = RelatedColumnsReference[i];
- iDest = relatedTable.Columns.IndexOf(src.ColumnName);
- if (iDest < 0)
- return null;
- relatedColumns[i] = relatedTable.Columns[iDest];
- }
- ForeignKeyConstraint clone = new ForeignKeyConstraint(ConstraintName,relatedColumns, columns);
- clone.UpdateRule = this.UpdateRule;
- clone.DeleteRule = this.DeleteRule;
- clone.AcceptRejectRule = this.AcceptRejectRule;
- // ...Extended Properties
- foreach(Object key in this.ExtendedProperties.Keys) {
- clone.ExtendedProperties[key]=this.ExtendedProperties[key];
- }
- return clone;
- }
- internal ForeignKeyConstraint Clone(DataTable destination) {
- Debug.Assert(this.Table == this.RelatedTable, "We call this clone just if we have the same datatable as parent and child ");
- int keys = Columns.Length;
- DataColumn[] columns = new DataColumn[keys];
- DataColumn[] relatedColumns = new DataColumn[keys];
- int iDest =0;
- for (int i = 0; i < keys; i++) {
- DataColumn src = Columns[i];
- iDest = destination.Columns.IndexOf(src.ColumnName);
- if (iDest < 0)
- return null;
- columns[i] = destination.Columns[iDest];
- src = RelatedColumnsReference[i];
- iDest = destination.Columns.IndexOf(src.ColumnName);
- if (iDest < 0)
- return null;
- relatedColumns[i] = destination.Columns[iDest];
- }
- ForeignKeyConstraint clone = new ForeignKeyConstraint(ConstraintName, relatedColumns, columns);
- clone.UpdateRule = this.UpdateRule;
- clone.DeleteRule = this.DeleteRule;
- clone.AcceptRejectRule = this.AcceptRejectRule;
- // ...Extended Properties
- foreach(Object key in this.ExtendedProperties.Keys) {
- clone.ExtendedProperties[key]=this.ExtendedProperties[key];
- }
- return clone;
- }
- private void Create(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns) {
- if (parentColumns.Length == 0 || childColumns.Length == 0)
- throw ExceptionBuilder.KeyLengthZero();
- if (parentColumns.Length != childColumns.Length)
- throw ExceptionBuilder.KeyLengthMismatch();
- for (int i = 0; i < parentColumns.Length; i++) {
- if (parentColumns[i].Computed) {
- throw ExceptionBuilder.ExpressionInConstraint(parentColumns[i]);
- }
- if (childColumns[i].Computed) {
- throw ExceptionBuilder.ExpressionInConstraint(childColumns[i]);
- }
- }
- this.parentKey = new DataKey(parentColumns, true);
- this.childKey = new DataKey(childColumns, true);
- ConstraintName = relationName;
- NonVirtualCheckState();
- }
- /// <devdoc>
- /// <para> Gets
- /// or sets the action that occurs across this constraint when a row is deleted.</para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- DefaultValue(Rule_Default),
- ResDescriptionAttribute(Res.ForeignKeyConstraintDeleteRuleDescr)
- ]
- public virtual Rule DeleteRule {
- get {
- CheckStateForProperty();
- return deleteRule;
- }
- set {
- switch(value) { // @perfnote: Enum.IsDefined
- case Rule.None:
- case Rule.Cascade:
- case Rule.SetNull:
- case Rule.SetDefault:
- this.deleteRule = value;
- break;
- default:
- throw Common.ADP.InvalidRule(value);
- }
- }
- }
- /// <devdoc>
- /// <para>Gets a value indicating whether the current <see cref='System.Data.ForeignKeyConstraint'/> is identical to the specified object.</para>
- /// </devdoc>
- public override bool Equals(object key) {
- if (!(key is ForeignKeyConstraint))
- return false;
- ForeignKeyConstraint key2 = (ForeignKeyConstraint) key;
- // The ParentKey and ChildKey completely identify the ForeignKeyConstraint
- return this.ParentKey.ColumnsEqual(key2.ParentKey) && this.ChildKey.ColumnsEqual(key2.ChildKey);
- }
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- public override Int32 GetHashCode() {
- return base.GetHashCode();
- }
- /// <devdoc>
- /// <para>
- /// The parent columns of this constraint.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.ForeignKeyConstraintParentColumnsDescr),
- ReadOnly(true)
- ]
- public virtual DataColumn[] RelatedColumns {
- get {
- CheckStateForProperty();
- return parentKey.ToArray();
- }
- }
- internal DataColumn[] RelatedColumnsReference {
- get {
- CheckStateForProperty();
- return parentKey.ColumnsReference;
- }
- }
- /// <devdoc>
- /// The internal key object for the parent table.
- /// </devdoc>
- internal DataKey ParentKey {
- get {
- CheckStateForProperty();
- return parentKey;
- }
- }
- internal DataRelation FindParentRelation () {
- DataRelationCollection rels = Table.ParentRelations;
- for (int i = 0; i < rels.Count; i++) {
- if (rels[i].ChildKeyConstraint == this)
- return rels[i];
- }
- return null;
- }
- /// <devdoc>
- /// <para>
- /// Gets the parent table of this constraint.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.ForeignKeyRelatedTableDescr),
- ReadOnly(true)
- ]
- public virtual DataTable RelatedTable {
- get {
- CheckStateForProperty();
- return parentKey.Table;
- }
- }
- /// <devdoc>
- /// <para>Gets or
- /// sets the action that occurs across this constraint on when a row is
- /// updated.</para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- DefaultValue(Rule_Default),
- ResDescriptionAttribute(Res.ForeignKeyConstraintUpdateRuleDescr)
- ]
- public virtual Rule UpdateRule {
- get {
- CheckStateForProperty();
- return updateRule;
- }
- set {
- switch(value) { // @perfnote: Enum.IsDefined
- case Rule.None:
- case Rule.Cascade:
- case Rule.SetNull:
- case Rule.SetDefault:
- this.updateRule = value;
- break;
- default:
- throw Common.ADP.InvalidRule(value);
- }
- }
- }
- }
- }
|