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.
47 [DefaultEvent ("CollectionChanged")]
48 [DefaultProperty ("Table")]
50 public abstract class DataRelationCollection : InternalDataCollectionBase
53 /// Summary description for DataTableRelationCollection.
55 internal class DataSetRelationCollection : DataRelationCollection
57 private DataSet dataSet;
60 /// Initializes a new instance of the DataSetRelationCollection class.
62 internal DataSetRelationCollection (DataSet dataSet)
64 this.dataSet = dataSet;
67 protected override DataSet GetDataSet()
73 /// Performs verification on the table.
75 /// <param name="relation">The relation to check.</param>
76 protected override void AddCore (DataRelation relation)
78 if (relation.ChildTable.DataSet != this.dataSet
79 || relation.ParentTable.DataSet != this.dataSet)
80 throw new DataException ();
82 base.AddCore (relation);
83 relation.ParentTable.ChildRelations.Add (relation);
84 relation.ChildTable.ParentRelations.Add (relation);
85 relation.SetDataSet (dataSet);
86 relation.UpdateConstraints ();
89 public override void AddRange (DataRelation[] relations)
91 base.AddRange (relations);
94 protected override void RemoveCore (DataRelation relation)
96 base.RemoveCore(relation);
97 relation.SetDataSet (null);
98 relation.ParentTable.ChildRelations.Remove (relation);
99 relation.ChildTable.ParentRelations.Remove (relation);
100 relation.SetParentKeyConstraint (null);
101 relation.SetChildKeyConstraint (null);
104 protected override ArrayList List {
112 /// Summary description for DataTableRelationCollection.
114 internal class DataTableRelationCollection : DataRelationCollection
116 private DataTable dataTable;
119 /// Initializes a new instance of the DataTableRelationCollection class.
121 internal DataTableRelationCollection (DataTable dataTable)
123 this.dataTable = dataTable;
126 protected override DataSet GetDataSet()
128 return dataTable.DataSet;
132 /// Performs verification on the table.
134 /// <param name="relation">The relation to check.</param>
135 protected override void AddCore (DataRelation relation)
137 if (dataTable.ParentRelations == this && relation.ChildTable != dataTable)
138 throw new ArgumentException ("Cannot add a relation to this table's " +
139 "ParentRelations where this table is not" +
140 " the Child table.");
142 if (dataTable.ChildRelations == this && relation.ParentTable != dataTable)
143 throw new ArgumentException("Cannot add a relation to this table's " +
144 "ChildRelations where this table is not" +
145 " the Parent table.");
147 dataTable.DataSet.Relations.Add(relation);
148 base.AddCore (relation);
152 protected override void RemoveCore (DataRelation relation)
154 relation.DataSet.Relations.Remove(relation);
155 base.RemoveCore (relation);
158 protected override ArrayList List {
165 private int defaultNameIndex;
166 private DataRelation inTransition;
171 /// Initializes a new instance of the DataRelationCollection class.
173 protected DataRelationCollection ()
177 defaultNameIndex = 1;
181 /// Gets the DataRelation object specified by name.
183 public DataRelation this [string name] {
185 int index = IndexOf (name, true);
186 return index < 0 ? null : (DataRelation) List[index];
191 /// Gets the DataRelation object at the specified index.
193 public DataRelation this [int index] {
195 if (index < 0 || index >= List.Count)
196 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
198 return (DataRelation)List [index];
204 private string GetNextDefaultRelationName ()
207 string defRelationName = "Relation" +index;
208 for (; Contains (defRelationName); ++index) {
209 defRelationName = "Relation" + index;
211 return defRelationName;
215 /// Adds a DataRelation to the DataRelationCollection.
217 /// <param name="relation">The DataRelation to add to the collection.</param>
219 public void Add(DataRelation relation)
221 // To prevent endless recursion
222 if(inTransition == relation) {
226 inTransition = relation;
231 this.AddCore (relation);
232 if(relation.RelationName == string.Empty)
233 relation.RelationName = GenerateRelationName();
235 relation.ParentTable.ResetPropertyDescriptorsCache();
236 relation.ChildTable.ResetPropertyDescriptorsCache();
238 CollectionChangeEventArgs e = new CollectionChangeEventArgs(CollectionChangeAction.Add, this);
239 OnCollectionChanged(e);
247 private string GenerateRelationName()
250 return "Relation" + index;
254 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
255 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
256 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
257 /// The CollectionChanged event is fired if it succeeds.
259 /// <param name="parentColumn">parent column of relation.</param>
260 /// <param name="childColumn">child column of relation.</param>
261 /// <returns>The created DataRelation.</returns>
262 public virtual DataRelation Add(DataColumn parentColumn, DataColumn childColumn)
264 DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumn, childColumn);
270 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
271 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
272 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
273 /// The CollectionChanged event is raised if it succeeds.
275 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
276 /// <param name="childColumns">An array of child DataColumn objects.</param>
277 /// <returns>The created DataRelation.</returns>
278 public virtual DataRelation Add(DataColumn[] parentColumns, DataColumn[] childColumns)
280 DataRelation dataRelation = new DataRelation(GetNextDefaultRelationName (), parentColumns, childColumns);
286 /// Creates a relation given the parameters and adds it to the collection.
287 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
288 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
289 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
290 /// The CollectionChanged event is raised if it succeeds.
292 /// <param name="name">The name of the relation.</param>
293 /// <param name="parentColumn">parent column of relation.</param>
294 /// <returns>The created DataRelation.</returns>
295 /// <returns></returns>
296 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn)
298 //If no name was supplied, give it a default name.
299 if (name == null || name == "") name = GetNextDefaultRelationName ();
301 DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn);
307 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
309 /// <param name="name">The name of the DataRelation to create.</param>
310 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
311 /// <param name="childColumns">An array of child DataColumn objects.</param>
312 /// <returns>The created DataRelation.</returns>
313 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns)
315 //If no name was supplied, give it a default name.
316 if (name == null || name == "") name = GetNextDefaultRelationName ();
318 DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns);
324 /// Creates a relation given the parameters and adds it to the collection.
325 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
326 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
327 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
328 /// The CollectionChanged event is raised if it succeeds.
330 /// <param name="name">The name of the relation.</param>
331 /// <param name="parentColumn">parent column of relation.</param>
332 /// <param name="childColumn">child column of relation.</param>
333 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
334 /// <returns>The created DataRelation.</returns>
335 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
337 //If no name was supplied, give it a default name.
338 if (name == null || name == "") name = GetNextDefaultRelationName ();
340 DataRelation dataRelation = new DataRelation(name, parentColumn, childColumn, createConstraints);
346 /// Creates a DataRelation with the specified name, arrays of parent and child columns,
347 /// and value specifying whether to create a constraint, and adds it to the collection.
349 /// <param name="name">The name of the DataRelation to create.</param>
350 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
351 /// <param name="childColumns">An array of child DataColumn objects.</param>
352 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
353 /// <returns>The created DataRelation.</returns>
354 public virtual DataRelation Add(string name, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints)
356 //If no name was supplied, give it a default name.
357 if (name == null || name == "") name = GetNextDefaultRelationName ();
359 DataRelation dataRelation = new DataRelation(name, parentColumns, childColumns, createConstraints);
368 /// <param name="relation">The relation to check.</param>
370 protected virtual void AddCore(DataRelation relation)
372 if (relation == null) {
373 //TODO: Issue a good exception message.
374 throw new ArgumentNullException();
376 if(List.IndexOf(relation) != -1) {
377 //TODO: Issue a good exception message.
378 throw new ArgumentException();
381 // check if the collection has a relation with the same name.
382 int tmp = IndexOf(relation.RelationName);
383 // if we found a relation with same name we have to check
384 // that it is the same case.
385 // indexof can return a table with different case letters.
387 relation.RelationName == this[tmp].RelationName)
388 throw new DuplicateNameException("A DataRelation named '" + relation.RelationName + "' already belongs to this DataSet.");
395 /// Copies the elements of the specified DataRelation array to the end of the collection.
397 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
398 public virtual void AddRange(DataRelation[] relations)
400 if (relations == null)
402 foreach (DataRelation relation in relations) Add(relation);
405 public virtual bool CanRemove(DataRelation relation)
407 if (relation == null || !GetDataSet().Equals(relation.DataSet))
410 // check if the relation doesnot belong to this collection
411 int tmp = IndexOf(relation.RelationName);
412 // if we found a relation with same name we have to check
413 // that it is the same case.
414 // indexof can return a table with different case letters.
416 if(relation.RelationName != this[tmp].RelationName)
426 public virtual void Clear()
428 for (int i = 0; i < Count; i++)
434 public virtual bool Contains(string name)
436 return (-1 != IndexOf (name, false));
439 private CollectionChangeEventArgs CreateCollectionChangeEvent (CollectionChangeAction action)
441 return new CollectionChangeEventArgs (action, this);
444 protected abstract DataSet GetDataSet();
446 public virtual int IndexOf(DataRelation relation)
448 return List.IndexOf(relation);
451 public virtual int IndexOf(string relationName)
453 return IndexOf(relationName, false);
456 private int IndexOf (string name, bool error)
458 int count = 0, match = -1;
459 for (int i = 0; i < List.Count; i++)
461 String name2 = ((DataRelation) List[i]).RelationName;
462 if (String.Compare (name, name2, true) == 0)
464 if (String.Compare (name, name2, false) == 0)
472 if (count > 1 && error)
473 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
477 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent)
479 if (CollectionChanged != null)
480 CollectionChanged (this, ccevent);
484 protected internal virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent)
486 throw new NotImplementedException ();
489 public void Remove (DataRelation relation)
491 // To prevent endless recursion
492 if(inTransition == relation) {
496 inTransition = relation;
499 if (relation == null)
504 // check if the list doesnot contains this relation.
505 if (!(List.Contains(relation)))
506 throw new ArgumentException("Relation doesnot belong to this Collection.");
508 RemoveCore (relation);
509 string name = "Relation" + index;
510 if (relation.RelationName == name)
513 OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
521 public void Remove (string name)
523 DataRelation relation = this[name];
524 if (relation == null)
525 throw new ArgumentException("Relation doesnot belong to this Collection.");
529 public void RemoveAt (int index)
531 DataRelation relation = this[index];
532 if (relation == null)
533 throw new IndexOutOfRangeException(String.Format("Cannot find relation {0}", index));
538 protected virtual void RemoveCore(DataRelation relation)
540 // Remove from collection
541 List.Remove(relation);
546 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
547 public event CollectionChangeEventHandler CollectionChanged;