2 // System.Data.DataRelationCollection.cs
5 // Christopher Podurgiel (cpodurgiel@msn.com)
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Tim Coleman (tim@timcoleman.com)
8 // Alan Tam Siu Lung <Tam@SiuLung.com>
10 // (C) Chris Podurgiel
11 // (C) 2002 Daniel Morgan
12 // Copyright (C) Tim Coleman, 2002
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 using System.Collections;
40 using System.ComponentModel;
42 namespace System.Data {
44 /// Represents the collection of DataRelation objects for this DataSet.
46 [Editor ("Microsoft.VSDesigner.Data.Design.DataRelationCollectionEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
47 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
48 [DefaultEvent ("CollectionChanged")]
49 [DefaultProperty ("Table")]
53 public abstract class DataRelationCollection : InternalDataCollectionBase
56 /// Summary description for DataTableRelationCollection.
58 internal class DataSetRelationCollection : DataRelationCollection
60 private DataSet dataSet;
61 DataRelation[] mostRecentRelations;
64 /// Initializes a new instance of the DataSetRelationCollection class.
66 internal DataSetRelationCollection (DataSet dataSet)
68 this.dataSet = dataSet;
71 protected override DataSet GetDataSet()
77 /// Performs verification on the table.
79 /// <param name="relation">The relation to check.</param>
80 protected override void AddCore (DataRelation relation)
82 if (relation.ChildTable.DataSet != this.dataSet
83 || relation.ParentTable.DataSet != this.dataSet)
84 throw new DataException ();
86 base.AddCore (relation);
87 relation.ParentTable.ChildRelations.Add (relation);
88 relation.ChildTable.ParentRelations.Add (relation);
89 relation.SetDataSet (dataSet);
90 relation.UpdateConstraints ();
93 protected override void RemoveCore (DataRelation relation)
95 base.RemoveCore(relation);
96 relation.SetDataSet (null);
97 relation.ParentTable.ChildRelations.Remove (relation);
98 relation.ChildTable.ParentRelations.Remove (relation);
99 relation.SetParentKeyConstraint (null);
100 relation.SetChildKeyConstraint (null);
103 public override void AddRange (DataRelation[] relations)
105 if (relations == null)
108 if (dataSet != null && dataSet.InitInProgress){
109 mostRecentRelations = relations;
113 foreach (DataRelation rel in relations){
120 internal override void PostAddRange ()
122 if (mostRecentRelations == null)
125 foreach (DataRelation rel in mostRecentRelations){
128 if (rel.InitInProgress)
129 rel.FinishInit (dataSet);
132 mostRecentRelations = null;
135 protected override ArrayList List {
143 /// Summary description for DataTableRelationCollection.
145 internal class DataTableRelationCollection : DataRelationCollection
147 private DataTable dataTable;
150 /// Initializes a new instance of the DataTableRelationCollection class.
152 internal DataTableRelationCollection (DataTable dataTable)
154 this.dataTable = dataTable;
157 protected override DataSet GetDataSet()
159 return dataTable.DataSet;
163 /// Performs verification on the table.
165 /// <param name="relation">The relation to check.</param>
166 protected override void AddCore (DataRelation relation)
168 if (dataTable.ParentRelations == this && relation.ChildTable != dataTable)
169 throw new ArgumentException ("Cannot add a relation to this table's " +
170 "ParentRelations where this table is not" +
171 " the Child table.");
173 if (dataTable.ChildRelations == this && relation.ParentTable != dataTable)
174 throw new ArgumentException("Cannot add a relation to this table's " +
175 "ChildRelations where this table is not" +
176 " the Parent table.");
178 dataTable.DataSet.Relations.Add(relation);
179 base.AddCore (relation);
183 protected override void RemoveCore (DataRelation relation)
185 relation.DataSet.Relations.Remove(relation);
186 base.RemoveCore (relation);
189 protected override ArrayList List {
196 private int defaultNameIndex;
197 private DataRelation inTransition;
202 /// Initializes a new instance of the DataRelationCollection class.
204 protected DataRelationCollection ()
208 defaultNameIndex = 1;
212 /// Gets the DataRelation object specified by name.
214 public DataRelation this [string name] {
216 int index = IndexOf (name, true);
217 return index < 0 ? null : (DataRelation) List[index];
222 /// Gets the DataRelation object at the specified index.
224 public DataRelation this [int index] {
226 if (index < 0 || index >= List.Count)
227 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
229 return (DataRelation)List [index];
235 private string GetNextDefaultRelationName ()
238 string defRelationName = "Relation" +index;
239 for (; Contains (defRelationName); ++index) {
240 defRelationName = "Relation" + index;
242 return defRelationName;
246 /// Adds a DataRelation to the DataRelationCollection.
248 /// <param name="relation">The DataRelation to add to the collection.</param>
250 public void Add(DataRelation relation)
252 // To prevent endless recursion
253 if(inTransition == relation) {
257 inTransition = relation;
262 this.AddCore (relation);
263 if(relation.RelationName == string.Empty)
264 relation.RelationName = GenerateRelationName();
266 relation.ParentTable.ResetPropertyDescriptorsCache();
267 relation.ChildTable.ResetPropertyDescriptorsCache();
269 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
270 OnCollectionChanged(e);
278 private string GenerateRelationName()
281 return "Relation" + index;
285 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
286 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
287 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
288 /// The CollectionChanged event is fired if it succeeds.
290 /// <param name="parentColumn">parent column of relation.</param>
291 /// <param name="childColumn">child column of relation.</param>
292 /// <returns>The created DataRelation.</returns>
293 public virtual DataRelation Add(DataColumn parentColumn, DataColumn childColumn)
295 DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumn, childColumn);
301 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
302 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
303 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
304 /// The CollectionChanged event is raised if it succeeds.
306 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
307 /// <param name="childColumns">An array of child DataColumn objects.</param>
308 /// <returns>The created DataRelation.</returns>
309 public virtual DataRelation Add(DataColumn[] parentColumns, DataColumn[] childColumns)
311 DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumns, childColumns);
317 /// Creates a relation given the parameters and adds it to the collection.
318 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
319 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
320 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
321 /// The CollectionChanged event is raised if it succeeds.
323 /// <param name="name">The name of the relation.</param>
324 /// <param name="parentColumn">parent column of relation.</param>
325 /// <returns>The created DataRelation.</returns>
326 /// <returns></returns>
327 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn)
329 //If no name was supplied, give it a default name.
330 if (name == null || name == "") name = GetNextDefaultRelationName ();
332 DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn);
338 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
340 /// <param name="name">The name of the DataRelation to create.</param>
341 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
342 /// <param name="childColumns">An array of child DataColumn objects.</param>
343 /// <returns>The created DataRelation.</returns>
344 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns)
346 //If no name was supplied, give it a default name.
347 if (name == null || name == "") name = GetNextDefaultRelationName ();
349 DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns);
355 /// Creates a relation given the parameters and adds it to the collection.
356 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
357 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
358 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
359 /// The CollectionChanged event is raised if it succeeds.
361 /// <param name="name">The name of the relation.</param>
362 /// <param name="parentColumn">parent column of relation.</param>
363 /// <param name="childColumn">child column of relation.</param>
364 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
365 /// <returns>The created DataRelation.</returns>
366 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
368 //If no name was supplied, give it a default name.
369 if (name == null || name == "") name = GetNextDefaultRelationName ();
371 DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn, createConstraints);
377 /// Creates a DataRelation with the specified name, arrays of parent and child columns,
378 /// and value specifying whether to create a constraint, and adds it to the collection.
380 /// <param name="name">The name of the DataRelation to create.</param>
381 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
382 /// <param name="childColumns">An array of child DataColumn objects.</param>
383 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
384 /// <returns>The created DataRelation.</returns>
385 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints)
387 //If no name was supplied, give it a default name.
388 if (name == null || name == "") name = GetNextDefaultRelationName ();
390 DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns, createConstraints);
399 /// <param name="relation">The relation to check.</param>
401 protected virtual void AddCore(DataRelation relation)
403 if (relation == null) {
404 //TODO: Issue a good exception message.
405 throw new ArgumentNullException();
407 if(List.IndexOf(relation) != -1) {
408 //TODO: Issue a good exception message.
409 throw new ArgumentException();
412 // check if the collection has a relation with the same name.
413 int tmp = IndexOf(relation.RelationName);
414 // if we found a relation with same name we have to check
415 // that it is the same case.
416 // indexof can return a table with different case letters.
418 relation.RelationName == this[tmp].RelationName)
419 throw new DuplicateNameException("A DataRelation named '" + relation.RelationName + "' already belongs to this DataSet.");
421 // check whether the relation exists between the columns already
422 foreach (DataRelation rel in this) {
423 // compare child columns
424 bool differs = false;
425 foreach (DataColumn current in relation.ChildColumns) {
427 foreach (DataColumn col in rel.ChildColumns) {
428 if (col == current) {
440 // compare parent columns
442 foreach (DataColumn current in relation.ParentColumns) {
444 foreach (DataColumn col in rel.ParentColumns) {
445 if (col == current) {
457 throw new ArgumentException ("A relation already exists for these child columns");
466 /// Copies the elements of the specified DataRelation array to the end of the collection.
468 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
469 public virtual void AddRange(DataRelation[] relations)
471 if (relations == null)
474 foreach (DataRelation relation in relations)
478 public void CopyTo (DataRelation [] array, int index)
480 CopyTo ((Array) array, index);
483 internal virtual void PostAddRange ()
487 public virtual bool CanRemove(DataRelation relation)
489 if (relation == null || !GetDataSet().Equals(relation.DataSet))
492 // check if the relation doesnot belong to this collection
493 int tmp = IndexOf(relation.RelationName);
494 // if we found a relation with same name we have to check
495 // that it is the same case.
496 // indexof can return a table with different case letters.
498 if(relation.RelationName != this[tmp].RelationName)
508 public virtual void Clear()
510 for (int i = 0; i < Count; i++)
516 public virtual bool Contains(string name)
518 return (-1 != IndexOf (name, false));
521 private CollectionChangeEventArgs CreateCollectionChangeEvent (CollectionChangeAction action)
523 return new CollectionChangeEventArgs (action, this);
526 protected abstract DataSet GetDataSet();
528 public virtual int IndexOf(DataRelation relation)
530 return List.IndexOf(relation);
533 public virtual int IndexOf(string relationName)
535 return IndexOf(relationName, false);
538 private int IndexOf (string name, bool error)
540 int count = 0, match = -1;
541 for (int i = 0; i < List.Count; i++)
543 String name2 = ((DataRelation) List[i]).RelationName;
544 if (String.Compare (name, name2, true) == 0)
546 if (String.Compare (name, name2, false) == 0)
554 if (count > 1 && error)
555 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
559 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent)
561 if (CollectionChanged != null)
562 CollectionChanged (this, ccevent);
566 protected internal virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent)
568 throw new NotImplementedException ();
571 public void Remove (DataRelation relation)
573 // To prevent endless recursion
574 if(inTransition == relation) {
578 inTransition = relation;
581 if (relation == null)
586 // check if the list doesnot contains this relation.
587 if (!(List.Contains(relation)))
588 throw new ArgumentException("Relation doesnot belong to this Collection.");
590 RemoveCore (relation);
591 string name = "Relation" + index;
592 if (relation.RelationName == name)
595 OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
603 public void Remove (string name)
605 DataRelation relation = this[name];
606 if (relation == null)
607 throw new ArgumentException("Relation doesnot belong to this Collection.");
611 public void RemoveAt (int index)
613 DataRelation relation = this[index];
614 if (relation == null)
615 throw new IndexOutOfRangeException(String.Format("Cannot find relation {0}", index));
620 protected virtual void RemoveCore(DataRelation relation)
622 // Remove from collection
623 List.Remove(relation);
628 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
629 public event CollectionChangeEventHandler CollectionChanged;