ForeignKeyConstraint.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. //------------------------------------------------------------------------------
  2. // <copyright file="ForeignKeyConstraint.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. // <owner current="false" primary="false">[....]</owner>
  8. //------------------------------------------------------------------------------
  9. namespace System.Data {
  10. using System;
  11. using System.ComponentModel;
  12. using System.Diagnostics;
  13. using System.Data.Common;
  14. /// <devdoc>
  15. /// <para>Represents an action
  16. /// restriction enforced on a set of columns in a primary key/foreign key relationship when
  17. /// a value or row is either deleted or updated.</para>
  18. /// </devdoc>
  19. [
  20. DefaultProperty("ConstraintName"),
  21. Editor("Microsoft.VSDesigner.Data.Design.ForeignKeyConstraintEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
  22. ]
  23. public class ForeignKeyConstraint : Constraint {
  24. // constants
  25. internal const Rule Rule_Default = Rule.Cascade;
  26. internal const AcceptRejectRule AcceptRejectRule_Default = AcceptRejectRule.None;
  27. // properties
  28. internal Rule deleteRule = Rule_Default;
  29. internal Rule updateRule = Rule_Default;
  30. internal AcceptRejectRule acceptRejectRule = AcceptRejectRule_Default;
  31. private DataKey childKey;
  32. private DataKey parentKey;
  33. // Design time serialization
  34. internal string constraintName = null;
  35. internal string[] parentColumnNames = null;
  36. internal string[] childColumnNames = null;
  37. internal string parentTableName = null;
  38. internal string parentTableNamespace = null;
  39. /// <devdoc>
  40. /// <para>
  41. /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified parent and
  42. /// child <see cref='System.Data.DataColumn'/> objects.
  43. /// </para>
  44. /// </devdoc>
  45. public ForeignKeyConstraint(DataColumn parentColumn, DataColumn childColumn)
  46. : this(null, parentColumn, childColumn) {
  47. }
  48. /// <devdoc>
  49. /// <para>
  50. /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified name,
  51. /// parent and child <see cref='System.Data.DataColumn'/> objects.
  52. /// </para>
  53. /// </devdoc>
  54. public ForeignKeyConstraint(string constraintName, DataColumn parentColumn, DataColumn childColumn) {
  55. DataColumn[] parentColumns = new DataColumn[] {parentColumn};
  56. DataColumn[] childColumns = new DataColumn[] {childColumn};
  57. Create(constraintName, parentColumns, childColumns);
  58. }
  59. /// <devdoc>
  60. /// <para>
  61. /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified arrays
  62. /// of parent and child <see cref='System.Data.DataColumn'/> objects.
  63. /// </para>
  64. /// </devdoc>
  65. public ForeignKeyConstraint(DataColumn[] parentColumns, DataColumn[] childColumns)
  66. : this(null, parentColumns, childColumns) {
  67. }
  68. /// <devdoc>
  69. /// <para>
  70. /// Initializes a new instance of the <see cref='System.Data.ForeignKeyConstraint'/> class with the specified name,
  71. /// and arrays of parent and child <see cref='System.Data.DataColumn'/> objects.
  72. /// </para>
  73. /// </devdoc>
  74. public ForeignKeyConstraint(string constraintName, DataColumn[] parentColumns, DataColumn[] childColumns) {
  75. Create(constraintName, parentColumns, childColumns);
  76. }
  77. // construct design time object
  78. /// <devdoc>
  79. /// <para>[To be supplied.]</para>
  80. /// </devdoc>
  81. [Browsable(false)]
  82. public ForeignKeyConstraint(string constraintName, string parentTableName, string[] parentColumnNames, string[] childColumnNames,
  83. AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule) {
  84. this.constraintName = constraintName;
  85. this.parentColumnNames = parentColumnNames;
  86. this.childColumnNames = childColumnNames;
  87. this.parentTableName = parentTableName;
  88. this.acceptRejectRule = acceptRejectRule;
  89. this.deleteRule = deleteRule;
  90. this.updateRule = updateRule;
  91. // ForeignKeyConstraint(constraintName, parentTableName, null, parentColumnNames, childColumnNames,acceptRejectRule, deleteRule, updateRule)
  92. }
  93. // construct design time object
  94. [Browsable(false)]
  95. public ForeignKeyConstraint(string constraintName, string parentTableName, string parentTableNamespace, string[] parentColumnNames,
  96. string[] childColumnNames, AcceptRejectRule acceptRejectRule, Rule deleteRule, Rule updateRule) {
  97. this.constraintName = constraintName;
  98. this.parentColumnNames = parentColumnNames;
  99. this.childColumnNames = childColumnNames;
  100. this.parentTableName = parentTableName;
  101. this.parentTableNamespace= parentTableNamespace;
  102. this.acceptRejectRule = acceptRejectRule;
  103. this.deleteRule = deleteRule;
  104. this.updateRule = updateRule;
  105. }
  106. /// <devdoc>
  107. /// The internal constraint object for the child table.
  108. /// </devdoc>
  109. internal DataKey ChildKey {
  110. get {
  111. CheckStateForProperty();
  112. return childKey;
  113. }
  114. }
  115. /// <devdoc>
  116. /// <para>
  117. /// Gets the child columns of this constraint.
  118. /// </para>
  119. /// </devdoc>
  120. [
  121. ResCategoryAttribute(Res.DataCategory_Data),
  122. ResDescriptionAttribute(Res.ForeignKeyConstraintChildColumnsDescr),
  123. ReadOnly(true)
  124. ]
  125. public virtual DataColumn[] Columns {
  126. get {
  127. CheckStateForProperty();
  128. return childKey.ToArray();
  129. }
  130. }
  131. /// <devdoc>
  132. /// <para>
  133. /// Gets the child table of this constraint.
  134. /// </para>
  135. /// </devdoc>
  136. [
  137. ResCategoryAttribute(Res.DataCategory_Data),
  138. ResDescriptionAttribute(Res.ConstraintTableDescr),
  139. ReadOnly(true)
  140. ]
  141. public override DataTable Table {
  142. get {
  143. CheckStateForProperty();
  144. return childKey.Table;
  145. }
  146. }
  147. internal string[] ParentColumnNames {
  148. get {
  149. return parentKey.GetColumnNames();
  150. }
  151. }
  152. internal string[] ChildColumnNames {
  153. get {
  154. return childKey.GetColumnNames();
  155. }
  156. }
  157. internal override void CheckCanAddToCollection(ConstraintCollection constraints) {
  158. if (Table != constraints.Table)
  159. throw ExceptionBuilder.ConstraintAddFailed(constraints.Table);
  160. if (Table.Locale.LCID != RelatedTable.Locale.LCID || Table.CaseSensitive != RelatedTable.CaseSensitive)
  161. throw ExceptionBuilder.CaseLocaleMismatch();
  162. }
  163. internal override bool CanBeRemovedFromCollection(ConstraintCollection constraints, bool fThrowException) {
  164. return true;
  165. }
  166. internal bool IsKeyNull( object[] values ) {
  167. for (int i = 0; i < values.Length; i++) {
  168. if (! DataStorage.IsObjectNull(values[i]))
  169. return false;
  170. }
  171. return true;
  172. }
  173. internal override bool IsConstraintViolated() {
  174. Index childIndex = childKey.GetSortIndex();
  175. object[] uniqueChildKeys = childIndex.GetUniqueKeyValues();
  176. bool errors = false;
  177. Index parentIndex = parentKey.GetSortIndex();
  178. for (int i = 0; i < uniqueChildKeys.Length; i++) {
  179. object[] childValues = (object[]) uniqueChildKeys[i];
  180. if (!IsKeyNull(childValues)) {
  181. if (!parentIndex.IsKeyInIndex(childValues)) {
  182. DataRow[] rows = childIndex.GetRows(childIndex.FindRecords(childValues));
  183. string error = Res.GetString(Res.DataConstraint_ForeignKeyViolation, ConstraintName, ExceptionBuilder.KeysToString(childValues));
  184. for (int j = 0; j < rows.Length; j++) {
  185. rows[j].RowError = error;
  186. }
  187. errors = true;
  188. }
  189. }
  190. }
  191. return errors;
  192. }
  193. internal override bool CanEnableConstraint() {
  194. if (Table.DataSet == null || !Table.DataSet.EnforceConstraints)
  195. return true;
  196. Index childIndex = childKey.GetSortIndex();
  197. object[] uniqueChildKeys = childIndex.GetUniqueKeyValues();
  198. Index parentIndex = parentKey.GetSortIndex();
  199. for (int i = 0; i < uniqueChildKeys.Length; i++) {
  200. object[] childValues = (object[]) uniqueChildKeys[i];
  201. if (!IsKeyNull(childValues) && !parentIndex.IsKeyInIndex(childValues)) {
  202. return false;
  203. }
  204. }
  205. return true;
  206. }
  207. internal void CascadeCommit(DataRow row) {
  208. if (row.RowState == DataRowState.Detached)
  209. return;
  210. if (acceptRejectRule == AcceptRejectRule.Cascade) {
  211. Index childIndex = childKey.GetSortIndex( row.RowState == DataRowState.Deleted ? DataViewRowState.Deleted : DataViewRowState.CurrentRows );
  212. object[] key = row.GetKeyValues(parentKey, row.RowState == DataRowState.Deleted ? DataRowVersion.Original : DataRowVersion.Default );
  213. if (IsKeyNull(key)) {
  214. return;
  215. }
  216. Range range = childIndex.FindRecords(key);
  217. if (!range.IsNull) {
  218. // SQLBU 499726 - DataTable internal index is corrupted: '13'
  219. // Self-referencing table has suspendIndexEvents, in the multi-table scenario the child table hasn't
  220. // this allows the self-ref table to maintain the index while in the child-table doesn't
  221. DataRow[] rows = childIndex.GetRows(range);
  222. foreach(DataRow childRow in rows) {
  223. if (DataRowState.Detached != childRow.RowState) {
  224. if (childRow.inCascade)
  225. continue;
  226. childRow.AcceptChanges();
  227. }
  228. }
  229. }
  230. }
  231. }
  232. internal void CascadeDelete(DataRow row) {
  233. if (-1 == row.newRecord)
  234. return;
  235. object[] currentKey = row.GetKeyValues(parentKey, DataRowVersion.Current);
  236. if (IsKeyNull(currentKey)) {
  237. return;
  238. }
  239. Index childIndex = childKey.GetSortIndex();
  240. switch (DeleteRule) {
  241. case Rule.None: {
  242. if (row.Table.DataSet.EnforceConstraints) {
  243. // if we're not cascading deletes, we should throw if we're going to strand a child row under enforceConstraints.
  244. Range range = childIndex.FindRecords(currentKey);
  245. if (!range.IsNull) {
  246. if (range.Count == 1 && childIndex.GetRow(range.Min) == row)
  247. return;
  248. throw ExceptionBuilder.FailedCascadeDelete(ConstraintName);
  249. }
  250. }
  251. break;
  252. }
  253. case Rule.Cascade: {
  254. object[] key = row.GetKeyValues(parentKey, DataRowVersion.Default);
  255. Range range = childIndex.FindRecords(key);
  256. if (!range.IsNull) {
  257. DataRow[] rows = childIndex.GetRows(range);
  258. for (int j = 0; j < rows.Length; j++) {
  259. DataRow r = rows[j];
  260. if (r.inCascade)
  261. continue;
  262. r.Table.DeleteRow(r);
  263. }
  264. }
  265. break;
  266. }
  267. case Rule.SetNull: {
  268. object[] proposedKey = new object[childKey.ColumnsReference.Length];
  269. for (int i = 0; i < childKey.ColumnsReference.Length; i++)
  270. proposedKey[i] = DBNull.Value;
  271. Range range = childIndex.FindRecords(currentKey);
  272. if (!range.IsNull) {
  273. DataRow[] rows = childIndex.GetRows(range);
  274. for (int j = 0; j < rows.Length; j++) {
  275. // if (rows[j].inCascade)
  276. // continue;
  277. if (row != rows[j])
  278. rows[j].SetKeyValues(childKey, proposedKey);
  279. }
  280. }
  281. break;
  282. }
  283. case Rule.SetDefault: {
  284. object[] proposedKey = new object[childKey.ColumnsReference.Length];
  285. for (int i = 0; i < childKey.ColumnsReference.Length; i++)
  286. proposedKey[i] = childKey.ColumnsReference[i].DefaultValue;
  287. Range range = childIndex.FindRecords(currentKey);
  288. if (!range.IsNull) {
  289. DataRow[] rows = childIndex.GetRows(range);
  290. for (int j = 0; j < rows.Length; j++) {
  291. // if (rows[j].inCascade)
  292. // continue;
  293. if (row != rows[j])
  294. rows[j].SetKeyValues(childKey, proposedKey);
  295. }
  296. }
  297. break;
  298. }
  299. default: {
  300. Debug.Assert(false, "Unknown Rule value");
  301. break;
  302. }
  303. }
  304. }
  305. internal void CascadeRollback(DataRow row) {
  306. Index childIndex = childKey.GetSortIndex( row.RowState == DataRowState.Deleted ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows);
  307. object[] key = row.GetKeyValues(parentKey, row.RowState == DataRowState.Modified ? DataRowVersion.Current : DataRowVersion.Default );
  308. // Bug : This is definitely not a proper fix. (Ref. MDAC Bug 73592)
  309. if (IsKeyNull(key)) {
  310. return;
  311. }
  312. Range range = childIndex.FindRecords(key);
  313. if (acceptRejectRule == AcceptRejectRule.Cascade) {
  314. if (!range.IsNull) {
  315. DataRow[] rows = childIndex.GetRows(range);
  316. for (int j = 0; j < rows.Length; j++) {
  317. if (rows[j].inCascade)
  318. continue;
  319. rows[j].RejectChanges();
  320. }
  321. }
  322. }
  323. else {
  324. // AcceptRejectRule.None
  325. if (row.RowState != DataRowState.Deleted && row.Table.DataSet.EnforceConstraints) {
  326. if (!range.IsNull) {
  327. if (range.Count == 1 && childIndex.GetRow(range.Min) == row)
  328. return;
  329. if (row.HasKeyChanged(parentKey)) {// if key is not changed, this will not cause child to be stranded
  330. throw ExceptionBuilder.FailedCascadeUpdate(ConstraintName);
  331. }
  332. }
  333. }
  334. }
  335. }
  336. internal void CascadeUpdate(DataRow row) {
  337. if (-1 == row.newRecord)
  338. return;
  339. object[] currentKey = row.GetKeyValues(parentKey, DataRowVersion.Current);
  340. if (!Table.DataSet.fInReadXml && IsKeyNull(currentKey)) {
  341. return;
  342. }
  343. Index childIndex = childKey.GetSortIndex();
  344. switch (UpdateRule) {
  345. case Rule.None: {
  346. if (row.Table.DataSet.EnforceConstraints)
  347. {
  348. // if we're not cascading deletes, we should throw if we're going to strand a child row under enforceConstraints.
  349. Range range = childIndex.FindRecords(currentKey);
  350. if (!range.IsNull) {
  351. throw ExceptionBuilder.FailedCascadeUpdate(ConstraintName);
  352. }
  353. }
  354. break;
  355. }
  356. case Rule.Cascade: {
  357. Range range = childIndex.FindRecords(currentKey);
  358. if (!range.IsNull) {
  359. object[] proposedKey = row.GetKeyValues(parentKey, DataRowVersion.Proposed);
  360. DataRow[] rows = childIndex.GetRows(range);
  361. for (int j = 0; j < rows.Length; j++) {
  362. // if (rows[j].inCascade)
  363. // continue;
  364. rows[j].SetKeyValues(childKey, proposedKey);
  365. }
  366. }
  367. break;
  368. }
  369. case Rule.SetNull: {
  370. object[] proposedKey = new object[childKey.ColumnsReference.Length];
  371. for (int i = 0; i < childKey.ColumnsReference.Length; i++)
  372. proposedKey[i] = DBNull.Value;
  373. Range range = childIndex.FindRecords(currentKey);
  374. if (!range.IsNull) {
  375. DataRow[] rows = childIndex.GetRows(range);
  376. for (int j = 0; j < rows.Length; j++) {
  377. // if (rows[j].inCascade)
  378. // continue;
  379. rows[j].SetKeyValues(childKey, proposedKey);
  380. }
  381. }
  382. break;
  383. }
  384. case Rule.SetDefault: {
  385. object[] proposedKey = new object[childKey.ColumnsReference.Length];
  386. for (int i = 0; i < childKey.ColumnsReference.Length; i++)
  387. proposedKey[i] = childKey.ColumnsReference[i].DefaultValue;
  388. Range range = childIndex.FindRecords(currentKey);
  389. if (!range.IsNull) {
  390. DataRow[] rows = childIndex.GetRows(range);
  391. for (int j = 0; j < rows.Length; j++) {
  392. // if (rows[j].inCascade)
  393. // continue;
  394. rows[j].SetKeyValues(childKey, proposedKey);
  395. }
  396. }
  397. break;
  398. }
  399. default: {
  400. Debug.Assert(false, "Unknown Rule value");
  401. break;
  402. }
  403. }
  404. }
  405. internal void CheckCanClearParentTable(DataTable table) {
  406. if (Table.DataSet.EnforceConstraints && Table.Rows.Count > 0) {
  407. throw ExceptionBuilder.FailedClearParentTable(table.TableName, ConstraintName, Table.TableName);
  408. }
  409. }
  410. internal void CheckCanRemoveParentRow(DataRow row) {
  411. Debug.Assert(Table.DataSet != null, "Relation " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening.");
  412. if (!Table.DataSet.EnforceConstraints)
  413. return;
  414. if (DataRelation.GetChildRows(this.ParentKey, this.ChildKey, row, DataRowVersion.Default).Length > 0) {
  415. throw ExceptionBuilder.RemoveParentRow(this);
  416. }
  417. }
  418. internal void CheckCascade(DataRow row, DataRowAction action) {
  419. Debug.Assert(Table.DataSet != null, "ForeignKeyConstraint " + ConstraintName + " isn't part of a DataSet, so this check shouldn't be happening.");
  420. if (row.inCascade)
  421. return;
  422. row.inCascade = true;
  423. try {
  424. if (action == DataRowAction.Change) {
  425. if (row.HasKeyChanged(parentKey)) {
  426. CascadeUpdate(row);
  427. }
  428. }
  429. else if (action == DataRowAction.Delete) {
  430. CascadeDelete(row);
  431. }
  432. else if (action == DataRowAction.Commit) {
  433. CascadeCommit(row);
  434. }
  435. else if (action == DataRowAction.Rollback) {
  436. CascadeRollback(row);
  437. }
  438. else if (action == DataRowAction.Add) {
  439. }
  440. else {
  441. Debug.Assert(false, "attempt to cascade unknown action: " + ((Enum) action).ToString());
  442. }
  443. }
  444. finally {
  445. row.inCascade = false;
  446. }
  447. }
  448. internal override void CheckConstraint(DataRow childRow, DataRowAction action) {
  449. if ((action == DataRowAction.Change ||
  450. action == DataRowAction.Add ||
  451. action == DataRowAction.Rollback) &&
  452. Table.DataSet != null && Table.DataSet.EnforceConstraints &&
  453. childRow.HasKeyChanged(childKey)) {
  454. // This branch is for cascading case verification.
  455. DataRowVersion version = (action == DataRowAction.Rollback) ? DataRowVersion.Original : DataRowVersion.Current;
  456. object[] childKeyValues = childRow.GetKeyValues(childKey);
  457. // check to see if this is just a change to my parent's proposed value.
  458. if (childRow.HasVersion(version)) {
  459. // this is the new proposed value for the parent.
  460. DataRow parentRow = DataRelation.GetParentRow(this.ParentKey, this.ChildKey, childRow, version);
  461. if(parentRow != null && parentRow.inCascade) {
  462. object[] parentKeyValues = parentRow.GetKeyValues(parentKey, action == DataRowAction.Rollback ? version : DataRowVersion.Default);
  463. int parentKeyValuesRecord = childRow.Table.NewRecord();
  464. childRow.Table.SetKeyValues(childKey, parentKeyValues, parentKeyValuesRecord);
  465. if (childKey.RecordsEqual(childRow.tempRecord, parentKeyValuesRecord)) {
  466. return;
  467. }
  468. }
  469. }
  470. // now check to see if someone exists... it will have to be in a parent row's current, not a proposed.
  471. object[] childValues = childRow.GetKeyValues(childKey);
  472. if (!IsKeyNull(childValues)) {
  473. Index parentIndex = parentKey.GetSortIndex();
  474. if (!parentIndex.IsKeyInIndex(childValues)) {
  475. // could be self-join constraint
  476. if (childKey.Table == parentKey.Table && childRow.tempRecord != -1) {
  477. int lo = 0;
  478. for (lo = 0; lo < childValues.Length; lo++) {
  479. DataColumn column = parentKey.ColumnsReference[lo];
  480. object value = column.ConvertValue(childValues[lo]);
  481. if (0 != column.CompareValueTo(childRow.tempRecord, value)) {
  482. break;
  483. }
  484. }
  485. if (lo == childValues.Length) {
  486. return;
  487. }
  488. }
  489. throw ExceptionBuilder.ForeignKeyViolation(ConstraintName, childKeyValues);
  490. }
  491. }
  492. }
  493. }
  494. private void NonVirtualCheckState () {
  495. if (_DataSet == null) {
  496. // Make sure columns arrays are valid
  497. parentKey.CheckState();
  498. childKey.CheckState();
  499. if (parentKey.Table.DataSet != childKey.Table.DataSet) {
  500. throw ExceptionBuilder.TablesInDifferentSets();
  501. }
  502. for (int i = 0; i < parentKey.ColumnsReference.Length; i++) {
  503. if (parentKey.ColumnsReference[i].DataType != childKey.ColumnsReference[i].DataType ||
  504. ((parentKey.ColumnsReference[i].DataType == typeof(DateTime)) && (parentKey.ColumnsReference[i].DateTimeMode != childKey.ColumnsReference[i].DateTimeMode) && ((parentKey.ColumnsReference[i].DateTimeMode & childKey.ColumnsReference[i].DateTimeMode) != DataSetDateTime.Unspecified)))
  505. throw ExceptionBuilder.ColumnsTypeMismatch();
  506. }
  507. if (childKey.ColumnsEqual(parentKey)) {
  508. throw ExceptionBuilder.KeyColumnsIdentical();
  509. }
  510. }
  511. }
  512. // If we're not in a DataSet relations collection, we need to verify on every property get that we're
  513. // still a good relation object.
  514. internal override void CheckState() {
  515. NonVirtualCheckState ();
  516. }
  517. /// <devdoc>
  518. /// <para>
  519. /// Indicates what kind of action should take place across
  520. /// this constraint when <see cref='System.Data.DataTable.AcceptChanges'/>
  521. /// is invoked.
  522. /// </para>
  523. /// </devdoc>
  524. [
  525. ResCategoryAttribute(Res.DataCategory_Data),
  526. DefaultValue(AcceptRejectRule_Default),
  527. ResDescriptionAttribute(Res.ForeignKeyConstraintAcceptRejectRuleDescr)
  528. ]
  529. public virtual AcceptRejectRule AcceptRejectRule {
  530. get {
  531. CheckStateForProperty();
  532. return acceptRejectRule;
  533. }
  534. set {
  535. switch(value) { // @perfnote: Enum.IsDefined
  536. case AcceptRejectRule.None:
  537. case AcceptRejectRule.Cascade:
  538. this.acceptRejectRule = value;
  539. break;
  540. default:
  541. throw Common.ADP.InvalidAcceptRejectRule(value);
  542. }
  543. }
  544. }
  545. internal override bool ContainsColumn(DataColumn column) {
  546. return parentKey.ContainsColumn(column) || childKey.ContainsColumn(column);
  547. }
  548. internal override Constraint Clone(DataSet destination) {
  549. return Clone(destination, false);
  550. }
  551. internal override Constraint Clone(DataSet destination, bool ignorNSforTableLookup) {
  552. int iDest;
  553. if (ignorNSforTableLookup) {
  554. iDest = destination.Tables.IndexOf(Table.TableName);
  555. }
  556. else {
  557. iDest = destination.Tables.IndexOf(Table.TableName, Table.Namespace, false); // pass false for last param
  558. // to be backward compatable, otherwise meay cause new exception
  559. }
  560. if (iDest < 0)
  561. return null;
  562. DataTable table = destination.Tables[iDest];
  563. if (ignorNSforTableLookup) {
  564. iDest = destination.Tables.IndexOf(RelatedTable.TableName);
  565. }
  566. else {
  567. iDest = destination.Tables.IndexOf(RelatedTable.TableName, RelatedTable.Namespace, false);// pass false for last param
  568. }
  569. if (iDest < 0)
  570. return null;
  571. DataTable relatedTable = destination.Tables[iDest];
  572. int keys = Columns.Length;
  573. DataColumn[] columns = new DataColumn[keys];
  574. DataColumn[] relatedColumns = new DataColumn[keys];
  575. for (int i = 0; i < keys; i++) {
  576. DataColumn src = Columns[i];
  577. iDest = table.Columns.IndexOf(src.ColumnName);
  578. if (iDest < 0)
  579. return null;
  580. columns[i] = table.Columns[iDest];
  581. src = RelatedColumnsReference[i];
  582. iDest = relatedTable.Columns.IndexOf(src.ColumnName);
  583. if (iDest < 0)
  584. return null;
  585. relatedColumns[i] = relatedTable.Columns[iDest];
  586. }
  587. ForeignKeyConstraint clone = new ForeignKeyConstraint(ConstraintName,relatedColumns, columns);
  588. clone.UpdateRule = this.UpdateRule;
  589. clone.DeleteRule = this.DeleteRule;
  590. clone.AcceptRejectRule = this.AcceptRejectRule;
  591. // ...Extended Properties
  592. foreach(Object key in this.ExtendedProperties.Keys) {
  593. clone.ExtendedProperties[key]=this.ExtendedProperties[key];
  594. }
  595. return clone;
  596. }
  597. internal ForeignKeyConstraint Clone(DataTable destination) {
  598. Debug.Assert(this.Table == this.RelatedTable, "We call this clone just if we have the same datatable as parent and child ");
  599. int keys = Columns.Length;
  600. DataColumn[] columns = new DataColumn[keys];
  601. DataColumn[] relatedColumns = new DataColumn[keys];
  602. int iDest =0;
  603. for (int i = 0; i < keys; i++) {
  604. DataColumn src = Columns[i];
  605. iDest = destination.Columns.IndexOf(src.ColumnName);
  606. if (iDest < 0)
  607. return null;
  608. columns[i] = destination.Columns[iDest];
  609. src = RelatedColumnsReference[i];
  610. iDest = destination.Columns.IndexOf(src.ColumnName);
  611. if (iDest < 0)
  612. return null;
  613. relatedColumns[i] = destination.Columns[iDest];
  614. }
  615. ForeignKeyConstraint clone = new ForeignKeyConstraint(ConstraintName, relatedColumns, columns);
  616. clone.UpdateRule = this.UpdateRule;
  617. clone.DeleteRule = this.DeleteRule;
  618. clone.AcceptRejectRule = this.AcceptRejectRule;
  619. // ...Extended Properties
  620. foreach(Object key in this.ExtendedProperties.Keys) {
  621. clone.ExtendedProperties[key]=this.ExtendedProperties[key];
  622. }
  623. return clone;
  624. }
  625. private void Create(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns) {
  626. if (parentColumns.Length == 0 || childColumns.Length == 0)
  627. throw ExceptionBuilder.KeyLengthZero();
  628. if (parentColumns.Length != childColumns.Length)
  629. throw ExceptionBuilder.KeyLengthMismatch();
  630. for (int i = 0; i < parentColumns.Length; i++) {
  631. if (parentColumns[i].Computed) {
  632. throw ExceptionBuilder.ExpressionInConstraint(parentColumns[i]);
  633. }
  634. if (childColumns[i].Computed) {
  635. throw ExceptionBuilder.ExpressionInConstraint(childColumns[i]);
  636. }
  637. }
  638. this.parentKey = new DataKey(parentColumns, true);
  639. this.childKey = new DataKey(childColumns, true);
  640. ConstraintName = relationName;
  641. NonVirtualCheckState();
  642. }
  643. /// <devdoc>
  644. /// <para> Gets
  645. /// or sets the action that occurs across this constraint when a row is deleted.</para>
  646. /// </devdoc>
  647. [
  648. ResCategoryAttribute(Res.DataCategory_Data),
  649. DefaultValue(Rule_Default),
  650. ResDescriptionAttribute(Res.ForeignKeyConstraintDeleteRuleDescr)
  651. ]
  652. public virtual Rule DeleteRule {
  653. get {
  654. CheckStateForProperty();
  655. return deleteRule;
  656. }
  657. set {
  658. switch(value) { // @perfnote: Enum.IsDefined
  659. case Rule.None:
  660. case Rule.Cascade:
  661. case Rule.SetNull:
  662. case Rule.SetDefault:
  663. this.deleteRule = value;
  664. break;
  665. default:
  666. throw Common.ADP.InvalidRule(value);
  667. }
  668. }
  669. }
  670. /// <devdoc>
  671. /// <para>Gets a value indicating whether the current <see cref='System.Data.ForeignKeyConstraint'/> is identical to the specified object.</para>
  672. /// </devdoc>
  673. public override bool Equals(object key) {
  674. if (!(key is ForeignKeyConstraint))
  675. return false;
  676. ForeignKeyConstraint key2 = (ForeignKeyConstraint) key;
  677. // The ParentKey and ChildKey completely identify the ForeignKeyConstraint
  678. return this.ParentKey.ColumnsEqual(key2.ParentKey) && this.ChildKey.ColumnsEqual(key2.ChildKey);
  679. }
  680. /// <devdoc>
  681. /// <para>[To be supplied.]</para>
  682. /// </devdoc>
  683. public override Int32 GetHashCode() {
  684. return base.GetHashCode();
  685. }
  686. /// <devdoc>
  687. /// <para>
  688. /// The parent columns of this constraint.
  689. /// </para>
  690. /// </devdoc>
  691. [
  692. ResCategoryAttribute(Res.DataCategory_Data),
  693. ResDescriptionAttribute(Res.ForeignKeyConstraintParentColumnsDescr),
  694. ReadOnly(true)
  695. ]
  696. public virtual DataColumn[] RelatedColumns {
  697. get {
  698. CheckStateForProperty();
  699. return parentKey.ToArray();
  700. }
  701. }
  702. internal DataColumn[] RelatedColumnsReference {
  703. get {
  704. CheckStateForProperty();
  705. return parentKey.ColumnsReference;
  706. }
  707. }
  708. /// <devdoc>
  709. /// The internal key object for the parent table.
  710. /// </devdoc>
  711. internal DataKey ParentKey {
  712. get {
  713. CheckStateForProperty();
  714. return parentKey;
  715. }
  716. }
  717. internal DataRelation FindParentRelation () {
  718. DataRelationCollection rels = Table.ParentRelations;
  719. for (int i = 0; i < rels.Count; i++) {
  720. if (rels[i].ChildKeyConstraint == this)
  721. return rels[i];
  722. }
  723. return null;
  724. }
  725. /// <devdoc>
  726. /// <para>
  727. /// Gets the parent table of this constraint.
  728. /// </para>
  729. /// </devdoc>
  730. [
  731. ResCategoryAttribute(Res.DataCategory_Data),
  732. ResDescriptionAttribute(Res.ForeignKeyRelatedTableDescr),
  733. ReadOnly(true)
  734. ]
  735. public virtual DataTable RelatedTable {
  736. get {
  737. CheckStateForProperty();
  738. return parentKey.Table;
  739. }
  740. }
  741. /// <devdoc>
  742. /// <para>Gets or
  743. /// sets the action that occurs across this constraint on when a row is
  744. /// updated.</para>
  745. /// </devdoc>
  746. [
  747. ResCategoryAttribute(Res.DataCategory_Data),
  748. DefaultValue(Rule_Default),
  749. ResDescriptionAttribute(Res.ForeignKeyConstraintUpdateRuleDescr)
  750. ]
  751. public virtual Rule UpdateRule {
  752. get {
  753. CheckStateForProperty();
  754. return updateRule;
  755. }
  756. set {
  757. switch(value) { // @perfnote: Enum.IsDefined
  758. case Rule.None:
  759. case Rule.Cascade:
  760. case Rule.SetNull:
  761. case Rule.SetDefault:
  762. this.updateRule = value;
  763. break;
  764. default:
  765. throw Common.ADP.InvalidRule(value);
  766. }
  767. }
  768. }
  769. }
  770. }