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 {
55 /// Summary description for DataTableRelationCollection.
57 internal class DataSetRelationCollection : DataRelationCollection {
58 private DataSet dataSet;
59 DataRelation [] mostRecentRelations;
62 /// Initializes a new instance of the DataSetRelationCollection class.
64 internal DataSetRelationCollection (DataSet dataSet)
66 this.dataSet = dataSet;
69 protected override DataSet GetDataSet ()
75 /// Performs verification on the table.
77 /// <param name="relation">The relation to check.</param>
78 protected override void AddCore (DataRelation relation)
80 if (relation.ChildTable.DataSet != dataSet || relation.ParentTable.DataSet != dataSet)
81 throw new DataException ();
83 base.AddCore (relation);
84 relation.ParentTable.ChildRelations.Add (relation);
85 relation.ChildTable.ParentRelations.Add (relation);
86 relation.SetDataSet (dataSet);
87 relation.UpdateConstraints ();
90 protected override void RemoveCore (DataRelation relation)
92 base.RemoveCore (relation);
93 relation.SetDataSet (null);
94 relation.ParentTable.ChildRelations.Remove (relation);
95 relation.ChildTable.ParentRelations.Remove (relation);
96 relation.SetParentKeyConstraint (null);
97 relation.SetChildKeyConstraint (null);
100 public override void AddRange (DataRelation [] relations)
102 if (relations == null)
105 if (dataSet != null && dataSet.InitInProgress){
106 mostRecentRelations = relations;
110 foreach (DataRelation rel in relations){
117 internal override void PostAddRange ()
119 if (mostRecentRelations == null)
122 foreach (DataRelation rel in mostRecentRelations){
125 if (rel.InitInProgress)
126 rel.FinishInit (dataSet);
129 mostRecentRelations = null;
132 protected override ArrayList List {
133 get { return base.List; }
136 public override DataRelation this [string name] {
138 int index = IndexOf (name, true);
139 return index < 0 ? null : (DataRelation) List [index];
144 /// Gets the DataRelation object at the specified index.
146 public override DataRelation this [int index] {
148 if (index < 0 || index >= List.Count)
149 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
151 return (DataRelation) List [index];
157 /// Summary description for DataTableRelationCollection.
159 internal class DataTableRelationCollection : DataRelationCollection {
160 private DataTable dataTable;
163 /// Initializes a new instance of the DataTableRelationCollection class.
165 internal DataTableRelationCollection (DataTable dataTable)
167 this.dataTable = dataTable;
170 protected override DataSet GetDataSet ()
172 return dataTable.DataSet;
175 public override DataRelation this [string name] {
177 int index = IndexOf (name, true);
178 return index < 0 ? null : (DataRelation) List [index];
183 /// Gets the DataRelation object at the specified index.
185 public override DataRelation this [int index] {
187 if (index < 0 || index >= List.Count)
188 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}.", index));
190 return (DataRelation) List [index];
195 /// Performs verification on the table.
197 /// <param name="relation">The relation to check.</param>
198 protected override void AddCore (DataRelation relation)
200 if (dataTable.ParentRelations == this && relation.ChildTable != dataTable)
201 throw new ArgumentException ("Cannot add a relation to this table's " +
202 "ParentRelations where this table is not" +
203 " the Child table.");
205 if (dataTable.ChildRelations == this && relation.ParentTable != dataTable)
206 throw new ArgumentException("Cannot add a relation to this table's " +
207 "ChildRelations where this table is not" +
208 " the Parent table.");
210 dataTable.DataSet.Relations.Add (relation);
211 base.AddCore (relation);
214 protected override void RemoveCore (DataRelation relation)
216 relation.DataSet.Relations.Remove(relation);
217 base.RemoveCore (relation);
220 protected override ArrayList List {
221 get { return base.List; }
225 private DataRelation inTransition;
230 /// Initializes a new instance of the DataRelationCollection class.
232 protected DataRelationCollection ()
238 /// Gets the DataRelation object specified by name.
240 public abstract DataRelation this [string name] {
245 /// Gets the DataRelation object at the specified index.
247 public abstract DataRelation this [int index] {
253 private string GetNextDefaultRelationName ()
256 string defRelationName = "Relation" + index;
257 for (; Contains (defRelationName); ++index)
258 defRelationName = "Relation" + index;
259 return defRelationName;
263 /// Adds a DataRelation to the DataRelationCollection.
265 /// <param name="relation">The DataRelation to add to the collection.</param>
266 public void Add (DataRelation relation)
268 // To prevent endless recursion
269 if (inTransition == relation)
272 inTransition = relation;
275 CollectionChangeEventArgs e = new CollectionChangeEventArgs (CollectionChangeAction.Add, this);
276 OnCollectionChanging (e);
278 this.AddCore (relation);
279 if (relation.RelationName == string.Empty)
280 relation.RelationName = GenerateRelationName ();
282 relation.ParentTable.ResetPropertyDescriptorsCache ();
283 relation.ChildTable.ResetPropertyDescriptorsCache ();
285 e = new CollectionChangeEventArgs (CollectionChangeAction.Add, this);
286 OnCollectionChanged (e);
292 private string GenerateRelationName ()
295 return "Relation" + index;
299 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
300 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
301 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
302 /// The CollectionChanged event is fired if it succeeds.
304 /// <param name="parentColumn">parent column of relation.</param>
305 /// <param name="childColumn">child column of relation.</param>
306 /// <returns>The created DataRelation.</returns>
307 public virtual DataRelation Add (DataColumn parentColumn, DataColumn childColumn)
309 DataRelation dataRelation = new DataRelation (GetNextDefaultRelationName (), parentColumn, childColumn);
315 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
316 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
317 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
318 /// The CollectionChanged event is raised if it succeeds.
320 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
321 /// <param name="childColumns">An array of child DataColumn objects.</param>
322 /// <returns>The created DataRelation.</returns>
323 public virtual DataRelation Add (DataColumn [] parentColumns, DataColumn [] childColumns)
325 DataRelation dataRelation = new DataRelation (GetNextDefaultRelationName (), parentColumns, childColumns);
331 /// Creates a relation given the parameters and adds it to the collection.
332 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
333 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
334 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
335 /// The CollectionChanged event is raised if it succeeds.
337 /// <param name="name">The name of the relation.</param>
338 /// <param name="parentColumn">parent column of relation.</param>
339 /// <returns>The created DataRelation.</returns>
340 /// <returns></returns>
341 public virtual DataRelation Add (string name, DataColumn parentColumn, DataColumn childColumn)
343 //If no name was supplied, give it a default name.
344 if (name == null || name == "")
345 name = GetNextDefaultRelationName ();
347 DataRelation dataRelation = new DataRelation (name, parentColumn, childColumn);
353 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
355 /// <param name="name">The name of the DataRelation to create.</param>
356 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
357 /// <param name="childColumns">An array of child DataColumn objects.</param>
358 /// <returns>The created DataRelation.</returns>
359 public virtual DataRelation Add (string name, DataColumn [] parentColumns, DataColumn [] childColumns)
361 //If no name was supplied, give it a default name.
362 if (name == null || name == "")
363 name = GetNextDefaultRelationName ();
365 DataRelation dataRelation = new DataRelation (name, parentColumns, childColumns);
371 /// Creates a relation given the parameters and adds it to the collection.
372 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
373 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
374 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
375 /// The CollectionChanged event is raised if it succeeds.
377 /// <param name="name">The name of the relation.</param>
378 /// <param name="parentColumn">parent column of relation.</param>
379 /// <param name="childColumn">child column of relation.</param>
380 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
381 /// <returns>The created DataRelation.</returns>
382 public virtual DataRelation Add(string name, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
384 //If no name was supplied, give it a default name.
385 if (name == null || name == "")
386 name = GetNextDefaultRelationName ();
388 DataRelation dataRelation = new DataRelation (name, parentColumn, childColumn, createConstraints);
394 /// Creates a DataRelation with the specified name, arrays of parent and child columns,
395 /// and value specifying whether to create a constraint, and adds it to the collection.
397 /// <param name="name">The name of the DataRelation to create.</param>
398 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
399 /// <param name="childColumns">An array of child DataColumn objects.</param>
400 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
401 /// <returns>The created DataRelation.</returns>
402 public virtual DataRelation Add (string name, DataColumn [] parentColumns, DataColumn [] childColumns, bool createConstraints)
404 //If no name was supplied, give it a default name.
405 if (name == null || name == "")
406 name = GetNextDefaultRelationName ();
408 DataRelation dataRelation = new DataRelation (name, parentColumns, childColumns, createConstraints);
417 /// <param name="relation">The relation to check.</param>
418 protected virtual void AddCore (DataRelation relation)
420 if (relation == null)
421 //TODO: Issue a good exception message.
422 throw new ArgumentNullException();
424 if(List.IndexOf (relation) != -1)
425 //TODO: Issue a good exception message.
426 throw new ArgumentException();
428 // check if the collection has a relation with the same name.
429 int tmp = IndexOf (relation.RelationName);
430 // if we found a relation with same name we have to check
431 // that it is the same case.
432 // indexof can return a table with different case letters.
433 if (tmp != -1 && relation.RelationName == this [tmp].RelationName)
434 throw new DuplicateNameException("A DataRelation named '" + relation.RelationName + "' already belongs to this DataSet.");
436 // check whether the relation exists between the columns already
437 foreach (DataRelation rel in this) {
438 // compare child columns
439 bool differs = false;
440 foreach (DataColumn current in relation.ChildColumns) {
442 foreach (DataColumn col in rel.ChildColumns) {
443 if (col == current) {
455 // compare parent columns
457 foreach (DataColumn current in relation.ParentColumns) {
459 foreach (DataColumn col in rel.ParentColumns) {
460 if (col == current) {
472 throw new ArgumentException ("A relation already exists for these child columns");
481 /// Copies the elements of the specified DataRelation array to the end of the collection.
483 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
484 public virtual void AddRange (DataRelation[] relations)
486 if (relations == null)
489 foreach (DataRelation relation in relations)
494 public void CopyTo (DataRelation [] array, int index)
496 CopyTo ((Array) array, index);
500 internal virtual void PostAddRange ()
504 public virtual bool CanRemove (DataRelation relation)
506 if (relation == null || !GetDataSet ().Equals (relation.DataSet))
509 // check if the relation doesnot belong to this collection
510 int tmp = IndexOf (relation.RelationName);
511 return tmp != -1 && relation.RelationName == this [tmp].RelationName;
514 public virtual void Clear ()
516 for (int i = 0; i < Count; i++)
522 public virtual bool Contains (string name)
524 DataSet tmpDataSet = GetDataSet ();
525 if (tmpDataSet != null) {
526 DataRelation tmpRelation = tmpDataSet.Relations [name];
527 if (tmpRelation != null)
530 return (-1 != IndexOf (name, false));
533 private CollectionChangeEventArgs CreateCollectionChangeEvent (CollectionChangeAction action)
535 return new CollectionChangeEventArgs (action, this);
538 protected abstract DataSet GetDataSet ();
540 public virtual int IndexOf (DataRelation relation)
542 return List.IndexOf (relation);
545 public virtual int IndexOf (string relationName)
547 return IndexOf (relationName, false);
550 private int IndexOf (string name, bool error)
552 int count = 0, match = -1;
553 for (int i = 0; i < List.Count; i++) {
554 String name2 = ((DataRelation) List[i]).RelationName;
555 if (String.Compare (name, name2, true) == 0) {
556 if (String.Compare (name, name2, false) == 0)
564 if (count > 1 && error)
565 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
569 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent)
571 if (CollectionChanged != null)
572 CollectionChanged (this, ccevent);
575 protected virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent)
577 // LAME Spec: No associated events and it doesn't update CollectionChanged
578 // event too as specified in MSDN
579 // throw new NotImplementedException ();
582 public void Remove (DataRelation relation)
584 // To prevent endless recursion
585 if (inTransition == relation)
588 inTransition = relation;
590 if (relation == null)
594 // check if the list doesnot contains this relation.
595 if (!(List.Contains (relation)))
596 throw new ArgumentException ("Relation doesnot belong to this Collection.");
598 OnCollectionChanging (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
600 RemoveCore (relation);
601 string name = "Relation" + index;
602 if (relation.RelationName == name)
605 OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction.Remove));
611 public void Remove (string name)
613 DataRelation relation = this [name];
614 if (relation == null)
615 throw new ArgumentException ("Relation doesnot belong to this Collection.");
619 public void RemoveAt (int index)
621 DataRelation relation = this [index];
622 if (relation == null)
623 throw new IndexOutOfRangeException (String.Format ("Cannot find relation {0}", index));
627 protected virtual void RemoveCore (DataRelation relation)
629 // Remove from collection
630 List.Remove (relation);
635 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
636 public event CollectionChangeEventHandler CollectionChanged;