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