DataRelationCollection.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DataRelationCollection.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.Collections;
  12. using System.ComponentModel;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. /// <devdoc>
  16. /// <para>
  17. /// Represents the collection of relations,
  18. /// each of which allows navigation between related parent/child tables.
  19. /// </para>
  20. /// </devdoc>
  21. [
  22. DefaultEvent("CollectionChanged"),
  23. Editor("Microsoft.VSDesigner.Data.Design.DataRelationCollectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
  24. DefaultProperty("Table"),
  25. ]
  26. public abstract class DataRelationCollection : InternalDataCollectionBase {
  27. private DataRelation inTransition = null;
  28. private int defaultNameIndex = 1;
  29. private CollectionChangeEventHandler onCollectionChangedDelegate;
  30. private CollectionChangeEventHandler onCollectionChangingDelegate;
  31. private static int _objectTypeCount; // Bid counter
  32. private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  33. internal int ObjectID {
  34. get {
  35. return _objectID;
  36. }
  37. }
  38. /// <devdoc>
  39. /// <para>Gets the relation specified by index.</para>
  40. /// </devdoc>
  41. public abstract DataRelation this[int index] {
  42. get;
  43. }
  44. /// <devdoc>
  45. /// <para>Gets the relation specified by name.</para>
  46. /// </devdoc>
  47. public abstract DataRelation this[string name] {
  48. get;
  49. }
  50. /// <devdoc>
  51. /// <para>
  52. /// Adds the relation to the collection.</para>
  53. /// </devdoc>
  54. public void Add(DataRelation relation) {
  55. IntPtr hscp;
  56. Bid.ScopeEnter(out hscp, "<ds.DataRelationCollection.Add|API> %d#, relation=%d\n", ObjectID, (relation != null) ? relation.ObjectID : 0);
  57. try {
  58. if (inTransition == relation)
  59. return;
  60. inTransition = relation;
  61. try {
  62. OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, relation));
  63. AddCore(relation);
  64. OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, relation));
  65. }
  66. finally {
  67. inTransition = null;
  68. }
  69. }
  70. finally{
  71. Bid.ScopeLeave(ref hscp);
  72. }
  73. }
  74. /// <devdoc>
  75. /// <para>[To be supplied.]</para>
  76. /// </devdoc>
  77. public virtual void AddRange(DataRelation[] relations) {
  78. if (relations != null) {
  79. foreach(DataRelation relation in relations) {
  80. if (relation != null) {
  81. Add(relation);
  82. }
  83. }
  84. }
  85. }
  86. /// <devdoc>
  87. /// <para>
  88. /// Creates a <see cref='System.Data.DataRelation'/> with the
  89. /// specified name, parent columns,
  90. /// child columns, and adds it to the collection.</para>
  91. /// </devdoc>
  92. public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns) {
  93. DataRelation relation = new DataRelation(name, parentColumns, childColumns);
  94. Add(relation);
  95. return relation;
  96. }
  97. /// <devdoc>
  98. /// Creates a relation given the parameters and adds it to the collection. An ArgumentNullException is
  99. /// thrown if this relation is null. An ArgumentException is thrown if this relation already belongs to
  100. /// this collection, belongs to another collection, or if this collection already has a relation with the
  101. /// same name (case insensitive).
  102. /// An InvalidRelationException is thrown if the relation can't be created based on the parameters.
  103. /// The CollectionChanged event is fired if it succeeds.
  104. /// </devdoc>
  105. public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) {
  106. DataRelation relation = new DataRelation(name, parentColumns, childColumns, createConstraints);
  107. Add(relation);
  108. return relation;
  109. }
  110. /// <devdoc>
  111. /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
  112. /// An ArgumentException is thrown if
  113. /// this relation already belongs to this collection or belongs to another collection.
  114. /// An InvalidConstraintException is thrown if the relation can't be created based on the parameters.
  115. /// The CollectionChanged event is fired if it succeeds.
  116. /// </devdoc>
  117. public virtual DataRelation Add(DataColumn[] parentColumns, DataColumn[] childColumns) {
  118. DataRelation relation = new DataRelation(null, parentColumns, childColumns);
  119. Add(relation);
  120. return relation;
  121. }
  122. /// <devdoc>
  123. /// Creates a relation given the parameters and adds it to the collection.
  124. /// An ArgumentException is thrown if this relation already belongs to
  125. /// this collection or belongs to another collection.
  126. /// A DuplicateNameException is thrown if this collection already has a relation with the same
  127. /// name (case insensitive).
  128. /// An InvalidConstraintException is thrown if the relation can't be created based on the parameters.
  129. /// The CollectionChanged event is fired if it succeeds.
  130. /// </devdoc>
  131. public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn) {
  132. DataRelation relation = new DataRelation(name, parentColumn, childColumn);
  133. Add(relation);
  134. return relation;
  135. }
  136. /// <devdoc>
  137. /// Creates a relation given the parameters and adds it to the collection.
  138. /// An ArgumentException is thrown if this relation already belongs to
  139. /// this collection or belongs to another collection.
  140. /// A DuplicateNameException is thrown if this collection already has a relation with the same
  141. /// name (case insensitive).
  142. /// An InvalidConstraintException is thrown if the relation can't be created based on the parameters.
  143. /// The CollectionChanged event is fired if it succeeds.
  144. /// </devdoc>
  145. public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints) {
  146. DataRelation relation = new DataRelation(name, parentColumn, childColumn, createConstraints);
  147. Add(relation);
  148. return relation;
  149. }
  150. /// <devdoc>
  151. /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
  152. /// An ArgumentException is thrown if
  153. /// this relation already belongs to this collection or belongs to another collection.
  154. /// An InvalidConstraintException is thrown if the relation can't be created based on the parameters.
  155. /// The CollectionChanged event is fired if it succeeds.
  156. /// </devdoc>
  157. public virtual DataRelation Add(DataColumn parentColumn, DataColumn childColumn) {
  158. DataRelation relation = new DataRelation(null, parentColumn, childColumn);
  159. Add(relation);
  160. return relation;
  161. }
  162. /// <devdoc>
  163. /// Does verification on the table.
  164. /// An ArgumentNullException is thrown if this relation is null. An ArgumentException is thrown if this relation
  165. /// already belongs to this collection, belongs to another collection.
  166. /// A DuplicateNameException is thrown if this collection already has a relation with the same
  167. /// name (case insensitive).
  168. /// </devdoc>
  169. protected virtual void AddCore(DataRelation relation) {
  170. Bid.Trace("<ds.DataRelationCollection.AddCore|INFO> %d#, relation=%d\n", ObjectID, (relation != null) ? relation.ObjectID : 0);
  171. if (relation == null)
  172. throw ExceptionBuilder.ArgumentNull("relation");
  173. relation.CheckState();
  174. DataSet dataSet = GetDataSet();
  175. if (relation.DataSet == dataSet)
  176. throw ExceptionBuilder.RelationAlreadyInTheDataSet();
  177. if (relation.DataSet != null)
  178. throw ExceptionBuilder.RelationAlreadyInOtherDataSet();
  179. if (relation.ChildTable.Locale.LCID != relation.ParentTable.Locale.LCID ||
  180. relation.ChildTable.CaseSensitive != relation.ParentTable.CaseSensitive)
  181. throw ExceptionBuilder.CaseLocaleMismatch();
  182. if (relation.Nested) {
  183. relation.CheckNamespaceValidityForNestedRelations(relation.ParentTable.Namespace);
  184. relation.ValidateMultipleNestedRelations();
  185. relation.ParentTable.ElementColumnCount++;
  186. }
  187. }
  188. /// <devdoc>
  189. /// <para>[To be supplied.]</para>
  190. /// </devdoc>
  191. [ResDescriptionAttribute(Res.collectionChangedEventDescr)]
  192. public event CollectionChangeEventHandler CollectionChanged {
  193. add {
  194. Bid.Trace("<ds.DataRelationCollection.add_CollectionChanged|API> %d#\n", ObjectID);
  195. onCollectionChangedDelegate += value;
  196. }
  197. remove {
  198. Bid.Trace("<ds.DataRelationCollection.remove_CollectionChanged|API> %d#\n", ObjectID);
  199. onCollectionChangedDelegate -= value;
  200. }
  201. }
  202. internal event CollectionChangeEventHandler CollectionChanging {
  203. add {
  204. Bid.Trace("<ds.DataRelationCollection.add_CollectionChanging|INFO> %d#\n", ObjectID);
  205. onCollectionChangingDelegate += value;
  206. }
  207. remove {
  208. Bid.Trace("<ds.DataRelationCollection.remove_CollectionChanging|INFO> %d#\n", ObjectID);
  209. onCollectionChangingDelegate -= value;
  210. }
  211. }
  212. /// <devdoc>
  213. /// Creates a new default name.
  214. /// </devdoc>
  215. internal string AssignName() {
  216. string newName = MakeName(defaultNameIndex);
  217. defaultNameIndex++;
  218. return newName;
  219. }
  220. /// <devdoc>
  221. /// Clears the collection of any relations.
  222. /// </devdoc>
  223. public virtual void Clear() {
  224. IntPtr hscp;
  225. Bid.ScopeEnter(out hscp, "<ds.DataRelationCollection.Clear|API> %d#\n", ObjectID);
  226. try {
  227. int count = Count;
  228. OnCollectionChanging(RefreshEventArgs);
  229. for (int i = count - 1; i >= 0; i--) {
  230. inTransition = this[i];
  231. RemoveCore(inTransition); // [....] : No need to go for try catch here and this will surely not throw any exception
  232. }
  233. OnCollectionChanged(RefreshEventArgs);
  234. inTransition = null;
  235. }
  236. finally{
  237. Bid.ScopeLeave(ref hscp);
  238. }
  239. }
  240. /// <devdoc>
  241. /// Returns true if this collection has a relation with the given name (case insensitive), false otherwise.
  242. /// </devdoc>
  243. public virtual bool Contains(string name) {
  244. return(InternalIndexOf(name) >= 0);
  245. }
  246. public void CopyTo(DataRelation[] array, int index) {
  247. if (array==null)
  248. throw ExceptionBuilder.ArgumentNull("array");
  249. if (index < 0)
  250. throw ExceptionBuilder.ArgumentOutOfRange("index");
  251. ArrayList alist = List;
  252. if (array.Length - index < alist.Count)
  253. throw ExceptionBuilder.InvalidOffsetLength();
  254. for(int i = 0; i < alist.Count; ++i) {
  255. array[index + i] = (DataRelation)alist[i];
  256. }
  257. }
  258. /// <devdoc>
  259. /// <para>
  260. /// Returns the index of a specified <see cref='System.Data.DataRelation'/>.
  261. /// </para>
  262. /// </devdoc>
  263. public virtual int IndexOf(DataRelation relation) {
  264. int relationCount = List.Count;
  265. for (int i = 0; i < relationCount; ++i) {
  266. if (relation == (DataRelation) List[i]) {
  267. return i;
  268. }
  269. }
  270. return -1;
  271. }
  272. /// <devdoc>
  273. /// <para>
  274. /// Returns the index of the
  275. /// relation with the given name (case insensitive), or -1 if the relation
  276. /// doesn't exist in the collection.
  277. /// </para>
  278. /// </devdoc>
  279. public virtual int IndexOf(string relationName) {
  280. int index = InternalIndexOf(relationName);
  281. return (index < 0) ? -1 : index;
  282. }
  283. internal int InternalIndexOf(string name) {
  284. int cachedI = -1;
  285. if ((null != name) && (0 < name.Length)) {
  286. int count = List.Count;
  287. int result = 0;
  288. for (int i = 0; i < count; i++) {
  289. DataRelation relation = (DataRelation) List[i];
  290. result = NamesEqual(relation.RelationName, name, false, GetDataSet().Locale);
  291. if (result == 1)
  292. return i;
  293. if (result == -1)
  294. cachedI = (cachedI == -1) ? i : -2;
  295. }
  296. }
  297. return cachedI;
  298. }
  299. /// <devdoc>
  300. /// Gets the dataSet for this collection.
  301. /// </devdoc>
  302. protected abstract DataSet GetDataSet();
  303. /// <devdoc>
  304. /// Makes a default name with the given index. e.g. Relation1, Relation2, ... Relationi
  305. /// </devdoc>
  306. private string MakeName(int index) {
  307. if (1 == index) {
  308. return "Relation1";
  309. }
  310. return "Relation" + index.ToString(System.Globalization.CultureInfo.InvariantCulture);
  311. }
  312. /// <devdoc>
  313. /// This method is called whenever the collection changes. Overriders
  314. /// of this method should call the base implementation of this method.
  315. /// </devdoc>
  316. protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent) {
  317. if (onCollectionChangedDelegate != null) {
  318. Bid.Trace("<ds.DataRelationCollection.OnCollectionChanged|INFO> %d#\n", ObjectID);
  319. onCollectionChangedDelegate(this, ccevent);
  320. }
  321. }
  322. /// <devdoc>
  323. /// <para>[To be supplied.]</para>
  324. /// </devdoc>
  325. protected virtual void OnCollectionChanging(CollectionChangeEventArgs ccevent) {
  326. if (onCollectionChangingDelegate != null) {
  327. Bid.Trace("<ds.DataRelationCollection.OnCollectionChanging|INFO> %d#\n", ObjectID);
  328. onCollectionChangingDelegate(this, ccevent);
  329. }
  330. }
  331. /// <devdoc>
  332. /// Registers this name as being used in the collection. Will throw an ArgumentException
  333. /// if the name is already being used. Called by Add, All property, and Relation.RelationName property.
  334. /// if the name is equivalent to the next default name to hand out, we increment our defaultNameIndex.
  335. /// </devdoc>
  336. internal void RegisterName(string name) {
  337. Bid.Trace("<ds.DataRelationCollection.RegisterName|INFO> %d#, name='%ls'\n", ObjectID, name);
  338. Debug.Assert (name != null);
  339. CultureInfo locale = GetDataSet().Locale;
  340. int relationCount = Count;
  341. for (int i = 0; i < relationCount; i++) {
  342. if (NamesEqual(name, this[i].RelationName, true, locale) != 0) {
  343. throw ExceptionBuilder.DuplicateRelation(this[i].RelationName);
  344. }
  345. }
  346. if (NamesEqual(name, MakeName(defaultNameIndex), true, locale) != 0) {
  347. defaultNameIndex++;
  348. }
  349. }
  350. /// <devdoc>
  351. /// <para>
  352. /// Verifies if a given relation can be removed from the collection.
  353. /// </para>
  354. /// </devdoc>
  355. public virtual bool CanRemove(DataRelation relation) {
  356. if (relation == null)
  357. return false;
  358. if (relation.DataSet != GetDataSet())
  359. return false;
  360. return true;
  361. }
  362. /// <devdoc>
  363. /// Removes the given relation from the collection.
  364. /// An ArgumentNullException is thrown if this relation is null. An ArgumentException is thrown
  365. /// if this relation doesn't belong to this collection.
  366. /// The CollectionChanged event is fired if it succeeds.
  367. /// </devdoc>
  368. public void Remove(DataRelation relation) {
  369. Bid.Trace("<ds.DataRelationCollection.Remove|API> %d#, relation=%d\n", ObjectID, (relation != null) ? relation.ObjectID : 0);
  370. if (inTransition == relation)
  371. return;
  372. inTransition = relation;
  373. try {
  374. OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Remove, relation));
  375. RemoveCore(relation);
  376. OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, relation));
  377. }
  378. finally {
  379. inTransition = null;
  380. }
  381. }
  382. /// <devdoc>
  383. /// Removes the relation at the given index from the collection. An IndexOutOfRangeException is
  384. /// thrown if this collection doesn't have a relation at this index.
  385. /// The CollectionChanged event is fired if it succeeds.
  386. /// </devdoc>
  387. public void RemoveAt(int index) {
  388. DataRelation dr = this[index];
  389. if (dr == null) {
  390. throw ExceptionBuilder.RelationOutOfRange(index);
  391. }
  392. else {
  393. Remove(dr);
  394. }
  395. }
  396. /// <devdoc>
  397. /// Removes the relation with the given name from the collection. An IndexOutOfRangeException is
  398. /// thrown if this collection doesn't have a relation with that name
  399. /// The CollectionChanged event is fired if it succeeds.
  400. /// </devdoc>
  401. public void Remove(string name) {
  402. DataRelation dr = this[name];
  403. if (dr == null) {
  404. throw ExceptionBuilder.RelationNotInTheDataSet(name);
  405. } else {
  406. Remove(dr);
  407. }
  408. }
  409. /// <devdoc>
  410. /// Does verification on the relation.
  411. /// An ArgumentNullException is thrown if this relation is null. An ArgumentException is thrown
  412. /// if this relation doesn't belong to this collection.
  413. /// </devdoc>
  414. protected virtual void RemoveCore(DataRelation relation) {
  415. Bid.Trace("<ds.DataRelationCollection.RemoveCore|INFO> %d#, relation=%d\n", ObjectID, (relation != null) ? relation.ObjectID : 0);
  416. if (relation == null)
  417. throw ExceptionBuilder.ArgumentNull("relation");
  418. DataSet dataSet = GetDataSet();
  419. if (relation.DataSet != dataSet)
  420. throw ExceptionBuilder.RelationNotInTheDataSet(relation.RelationName);
  421. if (relation.Nested) {
  422. relation.ParentTable.ElementColumnCount--;
  423. // webdata 103905
  424. // why we were not unregistering the table when removing the relation
  425. relation.ParentTable.Columns.UnregisterName(relation.ChildTable.TableName);
  426. }
  427. }
  428. /// <devdoc>
  429. /// Unregisters this name as no longer being used in the collection. Called by Remove, All property, and
  430. /// Relation.RelationName property. If the name is equivalent to the last proposed default name, we walk backwards
  431. /// to find the next proper default name to use.
  432. /// </devdoc>
  433. internal void UnregisterName(string name) {
  434. Bid.Trace("<ds.DataRelationCollection.UnregisterName|INFO> %d#, name='%ls'\n", ObjectID, name);
  435. if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, GetDataSet().Locale) != 0) {
  436. do {
  437. defaultNameIndex--;
  438. } while (defaultNameIndex > 1 &&
  439. !Contains(MakeName(defaultNameIndex - 1)));
  440. }
  441. }
  442. internal sealed class DataTableRelationCollection : DataRelationCollection {
  443. private readonly DataTable table;
  444. private readonly ArrayList relations; // For caching purpose only to improve performance
  445. private readonly bool fParentCollection;
  446. private CollectionChangeEventHandler onRelationPropertyChangedDelegate;
  447. internal DataTableRelationCollection(DataTable table, bool fParentCollection) {
  448. if (table == null)
  449. throw ExceptionBuilder.RelationTableNull();
  450. this.table = table;
  451. this.fParentCollection = fParentCollection;
  452. relations = new ArrayList();
  453. }
  454. protected override ArrayList List {
  455. get {
  456. return relations;
  457. }
  458. }
  459. private void EnsureDataSet() {
  460. if (table.DataSet == null) {
  461. throw ExceptionBuilder.RelationTableWasRemoved();
  462. }
  463. }
  464. protected override DataSet GetDataSet() {
  465. EnsureDataSet();
  466. return table.DataSet;
  467. }
  468. public override DataRelation this[int index] {
  469. get {
  470. if (index >= 0 && index < relations.Count)
  471. return (DataRelation)relations[index];
  472. else
  473. throw ExceptionBuilder.RelationOutOfRange(index);
  474. }
  475. }
  476. public override DataRelation this[string name] {
  477. get {
  478. int index = InternalIndexOf(name);
  479. if (index == -2) {
  480. throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
  481. }
  482. return (index < 0) ? null : (DataRelation)List[index];
  483. }
  484. }
  485. internal event CollectionChangeEventHandler RelationPropertyChanged {
  486. add {
  487. onRelationPropertyChangedDelegate += value;
  488. }
  489. remove {
  490. onRelationPropertyChangedDelegate -= value;
  491. }
  492. }
  493. internal void OnRelationPropertyChanged(CollectionChangeEventArgs ccevent) {
  494. if (!fParentCollection) {
  495. table.UpdatePropertyDescriptorCollectionCache();
  496. }
  497. if (onRelationPropertyChangedDelegate != null) {
  498. onRelationPropertyChangedDelegate(this, ccevent);
  499. }
  500. }
  501. private void AddCache(DataRelation relation) {
  502. relations.Add(relation);
  503. if (!fParentCollection) {
  504. table.UpdatePropertyDescriptorCollectionCache();
  505. }
  506. }
  507. protected override void AddCore(DataRelation relation) {
  508. if (fParentCollection) {
  509. if (relation.ChildTable != table)
  510. throw ExceptionBuilder.ChildTableMismatch();
  511. }
  512. else {
  513. if (relation.ParentTable != table)
  514. throw ExceptionBuilder.ParentTableMismatch();
  515. }
  516. // base.AddCore(relation); // Will be called from DataSet.Relations.AddCore
  517. GetDataSet().Relations.Add(relation);
  518. AddCache(relation);
  519. }
  520. public override bool CanRemove(DataRelation relation) {
  521. if (!base.CanRemove(relation))
  522. return false;
  523. if (fParentCollection) {
  524. if (relation.ChildTable != table)
  525. return false;
  526. }
  527. else {
  528. if (relation.ParentTable != table)
  529. return false;
  530. }
  531. return true;
  532. }
  533. private void RemoveCache(DataRelation relation) {
  534. for (int i = 0; i < relations.Count; i++) {
  535. if (relation == relations[i]) {
  536. relations.RemoveAt(i);
  537. if (!fParentCollection) {
  538. table.UpdatePropertyDescriptorCollectionCache();
  539. }
  540. return;
  541. }
  542. }
  543. throw ExceptionBuilder.RelationDoesNotExist();
  544. }
  545. protected override void RemoveCore(DataRelation relation) {
  546. if (fParentCollection) {
  547. if (relation.ChildTable != table)
  548. throw ExceptionBuilder.ChildTableMismatch();
  549. }
  550. else {
  551. if (relation.ParentTable != table)
  552. throw ExceptionBuilder.ParentTableMismatch();
  553. }
  554. // base.RemoveCore(relation); // Will be called from DataSet.Relations.RemoveCore
  555. GetDataSet().Relations.Remove(relation);
  556. RemoveCache(relation);
  557. }
  558. }
  559. internal sealed class DataSetRelationCollection : DataRelationCollection {
  560. private readonly DataSet dataSet;
  561. private readonly ArrayList relations;
  562. private DataRelation[] delayLoadingRelations = null;
  563. internal DataSetRelationCollection(DataSet dataSet) {
  564. if (dataSet == null)
  565. throw ExceptionBuilder.RelationDataSetNull();
  566. this.dataSet = dataSet;
  567. relations = new ArrayList();
  568. }
  569. protected override ArrayList List {
  570. get {
  571. return relations;
  572. }
  573. }
  574. public override void AddRange(DataRelation[] relations) {
  575. if (dataSet.fInitInProgress) {
  576. delayLoadingRelations = relations;
  577. return;
  578. }
  579. if (relations != null) {
  580. foreach(DataRelation relation in relations) {
  581. if (relation != null) {
  582. Add(relation);
  583. }
  584. }
  585. }
  586. }
  587. public override void Clear() {
  588. base.Clear();
  589. if (dataSet.fInitInProgress && delayLoadingRelations != null) {
  590. delayLoadingRelations = null;
  591. }
  592. }
  593. protected override DataSet GetDataSet() {
  594. return dataSet;
  595. }
  596. public override DataRelation this[int index] {
  597. get {
  598. if (index >= 0 && index < relations.Count)
  599. return (DataRelation)relations[index];
  600. else
  601. throw ExceptionBuilder.RelationOutOfRange(index);
  602. }
  603. }
  604. public override DataRelation this[string name] {
  605. get {
  606. int index = InternalIndexOf(name);
  607. if (index == -2) {
  608. throw ExceptionBuilder.CaseInsensitiveNameConflict(name);
  609. }
  610. return (index < 0) ? null : (DataRelation)List[index];
  611. }
  612. }
  613. protected override void AddCore(DataRelation relation) {
  614. base.AddCore(relation);
  615. if (relation.ChildTable.DataSet != dataSet || relation.ParentTable.DataSet != dataSet)
  616. throw ExceptionBuilder.ForeignRelation();
  617. relation.CheckState();
  618. if(relation.Nested) {
  619. relation.CheckNestedRelations();
  620. }
  621. if (relation.relationName.Length == 0)
  622. relation.relationName = AssignName();
  623. else
  624. RegisterName(relation.relationName);
  625. DataKey childKey = relation.ChildKey;
  626. for (int i = 0; i < relations.Count; i++) {
  627. if (childKey.ColumnsEqual(((DataRelation)relations[i]).ChildKey)) {
  628. if (relation.ParentKey.ColumnsEqual(((DataRelation)relations[i]).ParentKey))
  629. throw ExceptionBuilder.RelationAlreadyExists();
  630. }
  631. }
  632. relations.Add(relation);
  633. ((DataRelationCollection.DataTableRelationCollection)(relation.ParentTable.ChildRelations)).Add(relation); // Caching in ParentTable -> ChildRelations
  634. ((DataRelationCollection.DataTableRelationCollection)(relation.ChildTable.ParentRelations)).Add(relation); // Caching in ChildTable -> ParentRelations
  635. relation.SetDataSet(dataSet);
  636. relation.ChildKey.GetSortIndex().AddRef();
  637. if (relation.Nested) {
  638. relation.ChildTable.CacheNestedParent();
  639. }
  640. ForeignKeyConstraint foreignKey = relation.ChildTable.Constraints.FindForeignKeyConstraint(relation.ParentColumnsReference, relation.ChildColumnsReference);
  641. if (relation.createConstraints) {
  642. if (foreignKey == null) {
  643. relation.ChildTable.Constraints.Add(foreignKey = new ForeignKeyConstraint(relation.ParentColumnsReference, relation.ChildColumnsReference));
  644. // try to name the fk constraint the same as the parent relation:
  645. try {
  646. foreignKey.ConstraintName = relation.RelationName;
  647. }
  648. catch (Exception e) {
  649. //
  650. if (!Common.ADP.IsCatchableExceptionType(e)) {
  651. throw;
  652. }
  653. ExceptionBuilder.TraceExceptionWithoutRethrow(e);
  654. // ignore the exception
  655. }
  656. }
  657. }
  658. UniqueConstraint key = relation.ParentTable.Constraints.FindKeyConstraint(relation.ParentColumnsReference);
  659. relation.SetParentKeyConstraint(key);
  660. relation.SetChildKeyConstraint(foreignKey);
  661. }
  662. protected override void RemoveCore(DataRelation relation) {
  663. base.RemoveCore(relation);
  664. dataSet.OnRemoveRelationHack(relation);
  665. relation.SetDataSet(null);
  666. relation.ChildKey.GetSortIndex().RemoveRef();
  667. if (relation.Nested) {
  668. relation.ChildTable.CacheNestedParent();
  669. }
  670. for (int i = 0; i < relations.Count; i++) {
  671. if (relation == relations[i]) {
  672. relations.RemoveAt(i);
  673. ((DataRelationCollection.DataTableRelationCollection)(relation.ParentTable.ChildRelations)).Remove(relation); // Remove Cache from ParentTable -> ChildRelations
  674. ((DataRelationCollection.DataTableRelationCollection)(relation.ChildTable.ParentRelations)).Remove(relation); // Removing Cache from ChildTable -> ParentRelations
  675. if (relation.Nested)
  676. relation.ChildTable.CacheNestedParent();
  677. UnregisterName(relation.RelationName);
  678. relation.SetParentKeyConstraint(null);
  679. relation.SetChildKeyConstraint(null);
  680. return;
  681. }
  682. }
  683. throw ExceptionBuilder.RelationDoesNotExist();
  684. }
  685. internal void FinishInitRelations() {
  686. if (delayLoadingRelations == null)
  687. return;
  688. DataRelation rel;
  689. int colCount;
  690. DataColumn[] parents, childs;
  691. for (int i = 0; i < delayLoadingRelations.Length; i++) {
  692. rel = delayLoadingRelations[i];
  693. if (rel.parentColumnNames == null || rel.childColumnNames == null) {
  694. this.Add(rel);
  695. continue;
  696. }
  697. colCount = rel.parentColumnNames.Length;
  698. parents = new DataColumn[colCount];
  699. childs = new DataColumn[colCount];
  700. for (int j = 0; j < colCount; j++) {
  701. if (rel.parentTableNamespace == null)
  702. parents[j] = dataSet.Tables[rel.parentTableName].Columns[rel.parentColumnNames[j]];
  703. else
  704. parents[j] = dataSet.Tables[rel.parentTableName, rel.parentTableNamespace].Columns[rel.parentColumnNames[j]];
  705. if (rel.childTableNamespace == null)
  706. childs[j] = dataSet.Tables[rel.childTableName].Columns[rel.childColumnNames[j]];
  707. else
  708. childs[j] = dataSet.Tables[rel.childTableName, rel.childTableNamespace].Columns[rel.childColumnNames[j]];
  709. }
  710. DataRelation newRelation = new DataRelation(rel.relationName, parents, childs, false);
  711. newRelation.Nested = rel.nested;
  712. this.Add(newRelation);
  713. }
  714. delayLoadingRelations = null;
  715. }
  716. }
  717. }
  718. }