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
;
68 /// Gets the DataRelation object specified by name.
70 public override DataRelation
this [string name
]
73 int index
= IndexOf (name
, true);
74 return index
< 0 ? null : (DataRelation
) List
[index
];
79 /// Gets the DataRelation object at the specified index.
81 public override DataRelation
this [int index
]
85 return List
[index
] as DataRelation
;
86 } catch (ArgumentOutOfRangeException e
) {
87 throw new IndexOutOfRangeException (String
.Format ("Cannot find relation {0}.", index
));
92 protected override DataSet
GetDataSet()
98 /// Performs verification on the table.
100 /// <param name="relation">The relation to check.</param>
101 protected override void AddCore (DataRelation relation
)
103 base.AddCore (relation
);
104 if (relation
.ChildTable
.DataSet
!= this.dataSet
|| relation
.ParentTable
.DataSet
!= this.dataSet
)
105 throw new DataException ();
106 relation
.SetDataSet (dataSet
);
107 relation
.ParentTable
.ChildRelations
.Add (relation
);
108 relation
.ChildTable
.ParentRelations
.Add (relation
);
109 ForeignKeyConstraint foreignKeyConstraint
= null;
111 if (relation
.createConstraints
)
114 UniqueConstraint uniqueConstraint
= null;
115 ConstraintCollection parentConstrains
= relation
.ParentTable
.Constraints
;
116 // find if the unique constraint already exists in the parent table.
117 foreach (Constraint o
in parentConstrains
)
119 if (o
is UniqueConstraint
)
121 UniqueConstraint uc
= (UniqueConstraint
) o
;
122 if (uc
.Columns
.Length
== relation
.ParentColumns
.Length
)
124 bool allColumnsEqual
= true;
125 for (int columnCnt
= 0; columnCnt
< uc
.Columns
.Length
; ++columnCnt
)
127 if (uc
.Columns
[columnCnt
] != relation
.ParentColumns
[columnCnt
])
129 allColumnsEqual
= false;
135 uniqueConstraint
= uc
;
141 // if we did not find the unique constraint in the parent table.
142 // we generate new uniqueconastraint and add it to the parent table.
143 if (uniqueConstraint
== null)
145 uniqueConstraint
= new UniqueConstraint(relation
.ParentColumns
, false);
146 relation
.ParentTable
.Constraints
.Add(uniqueConstraint
);
149 foreignKeyConstraint
= new ForeignKeyConstraint (relation
.RelationName
, relation
.ParentColumns
, relation
.ChildColumns
);
150 relation
.ChildTable
.Constraints
.Add (foreignKeyConstraint
);
152 relation
.SetParentKeyConstraint (uniqueConstraint
);
153 relation
.SetChildKeyConstraint (foreignKeyConstraint
);
159 public override void AddRange (DataRelation
[] relations
)
161 base.AddRange (relations
);
164 public override void Clear ()
166 for (int i
= 0; i
< Count
; i
++)
172 protected override void RemoveCore (DataRelation relation
)
174 relation
.SetDataSet (null);
175 relation
.ParentTable
.ChildRelations
.Remove (relation
);
176 relation
.ChildTable
.ParentRelations
.Remove (relation
);
177 relation
.SetParentKeyConstraint (null);
178 relation
.SetChildKeyConstraint (null);
181 protected override ArrayList List
{
189 /// Summary description for DataTableRelationCollection.
191 internal class DataTableRelationCollection
: DataRelationCollection
193 private DataTable dataTable
;
196 /// Initializes a new instance of the DataTableRelationCollection class.
198 internal DataTableRelationCollection (DataTable dataTable
)
200 this.dataTable
= dataTable
;
204 /// Gets the DataRelation object specified by name.
206 public override DataRelation
this [string name
]
209 int index
= IndexOf (name
, true);
210 return index
< 0 ? null : (DataRelation
) List
[index
];
215 /// Gets the DataRelation object at the specified index.
217 public override DataRelation
this [int index
]
221 return List
[index
] as DataRelation
;
222 } catch (ArgumentOutOfRangeException e
) {
223 throw new IndexOutOfRangeException (String
.Format ("Cannot find relation {0}.", index
));
228 protected override DataSet
GetDataSet()
230 return dataTable
.DataSet
;
233 protected override void AddCore (DataRelation relation
)
235 base.AddCore (relation
);
238 protected override void RemoveCore (DataRelation relation
)
240 base.RemoveCore (relation
);
243 protected override ArrayList List
{
250 private int defaultNameIndex
;
251 private bool inTransition
;
256 /// Initializes a new instance of the DataRelationCollection class.
258 protected DataRelationCollection ()
261 defaultNameIndex
= 1;
262 inTransition
= false;
266 /// Gets the DataRelation object specified by name.
268 public abstract DataRelation
this[string name
]{get;}
271 /// Gets the DataRelation object at the specified index.
273 public abstract DataRelation
this[int index
]{get;}
277 private string GetNextDefaultRelationName ()
280 string defRelationName
= "Relation" +index
;
281 for (; Contains (defRelationName
); ++index
) {
282 defRelationName
= "Relation" + index
;
284 return defRelationName
;
288 /// Adds a DataRelation to the DataRelationCollection.
290 /// <param name="relation">The DataRelation to add to the collection.</param>
292 public void Add(DataRelation relation
)
294 this.AddCore (relation
);
295 if(relation
.RelationName
== string.Empty
)
296 relation
.RelationName
= GenerateRelationName();
297 CollectionChangeEventArgs e
= new CollectionChangeEventArgs(CollectionChangeAction
.Add
, this);
298 //List.Add(relation);
299 OnCollectionChanged(e
);
302 private string GenerateRelationName()
305 return "Relation" + index
;
309 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
310 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
311 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
312 /// The CollectionChanged event is fired if it succeeds.
314 /// <param name="parentColumn">parent column of relation.</param>
315 /// <param name="childColumn">child column of relation.</param>
316 /// <returns>The created DataRelation.</returns>
317 public virtual DataRelation
Add(DataColumn parentColumn
, DataColumn childColumn
)
319 DataRelation dataRelation
= new DataRelation(GetNextDefaultRelationName (), parentColumn
, childColumn
);
325 /// Creates a relation given the parameters and adds it to the collection. The name is defaulted.
326 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
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="parentColumns">An array of parent DataColumn objects.</param>
331 /// <param name="childColumns">An array of child DataColumn objects.</param>
332 /// <returns>The created DataRelation.</returns>
333 public virtual DataRelation
Add(DataColumn
[] parentColumns
, DataColumn
[] childColumns
)
335 DataRelation dataRelation
= new DataRelation(GetNextDefaultRelationName (), parentColumns
, childColumns
);
341 /// Creates a relation given the parameters and adds it to the collection.
342 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
343 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
344 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
345 /// The CollectionChanged event is raised if it succeeds.
347 /// <param name="name">The name of the relation.</param>
348 /// <param name="parentColumn">parent column of relation.</param>
349 /// <returns>The created DataRelation.</returns>
350 /// <returns></returns>
351 public virtual DataRelation
Add(string name
, DataColumn parentColumn
, DataColumn childColumn
)
353 //If no name was supplied, give it a default name.
354 if (name
== null || name
== "") name
= GetNextDefaultRelationName ();
356 DataRelation dataRelation
= new DataRelation(name
, parentColumn
, childColumn
);
362 /// Creates a DataRelation with the specified name, and arrays of parent and child columns, and adds it to the collection.
364 /// <param name="name">The name of the DataRelation to create.</param>
365 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
366 /// <param name="childColumns">An array of child DataColumn objects.</param>
367 /// <returns>The created DataRelation.</returns>
368 public virtual DataRelation
Add(string name
, DataColumn
[] parentColumns
, DataColumn
[] childColumns
)
370 //If no name was supplied, give it a default name.
371 if (name
== null || name
== "") name
= GetNextDefaultRelationName ();
373 DataRelation dataRelation
= new DataRelation(name
, parentColumns
, childColumns
);
379 /// Creates a relation given the parameters and adds it to the collection.
380 /// An ArgumentException is generated if this relation already belongs to this collection or belongs to another collection.
381 /// A DuplicateNameException is generated if this collection already has a relation with the same name (case insensitive).
382 /// An InvalidConstraintException is generated if the relation can't be created based on the parameters.
383 /// The CollectionChanged event is raised if it succeeds.
385 /// <param name="name">The name of the relation.</param>
386 /// <param name="parentColumn">parent column of relation.</param>
387 /// <param name="childColumn">child column of relation.</param>
388 /// <param name="createConstraints">true to create constraints; otherwise false. (default is true)</param>
389 /// <returns>The created DataRelation.</returns>
390 public virtual DataRelation
Add(string name
, DataColumn parentColumn
, DataColumn childColumn
, bool createConstraints
)
392 //If no name was supplied, give it a default name.
393 if (name
== null || name
== "") name
= GetNextDefaultRelationName ();
395 DataRelation dataRelation
= new DataRelation(name
, parentColumn
, childColumn
, createConstraints
);
401 /// Creates a DataRelation with the specified name, arrays of parent and child columns,
402 /// and value specifying whether to create a constraint, and adds it to the collection.
404 /// <param name="name">The name of the DataRelation to create.</param>
405 /// <param name="parentColumns">An array of parent DataColumn objects.</param>
406 /// <param name="childColumns">An array of child DataColumn objects.</param>
407 /// <param name="createConstraints">true to create a constraint; otherwise false.</param>
408 /// <returns>The created DataRelation.</returns>
409 public virtual DataRelation
Add(string name
, DataColumn
[] parentColumns
, DataColumn
[] childColumns
, bool createConstraints
)
411 //If no name was supplied, give it a default name.
412 if (name
== null || name
== "") name
= GetNextDefaultRelationName ();
414 DataRelation dataRelation
= new DataRelation(name
, parentColumns
, childColumns
, createConstraints
);
421 /// Performs verification on the table.
423 /// <param name="relation">The relation to check.</param>
425 protected virtual void AddCore(DataRelation relation
)
427 if (relation
== null)
429 //TODO: Issue a good exception message.
430 throw new ArgumentNullException();
432 if(List
.IndexOf(relation
) != -1)
434 //TODO: Issue a good exception message.
435 throw new ArgumentException();
438 // check if the collection has a relation with the same name.
439 int tmp
= IndexOf(relation
.RelationName
);
440 // if we found a relation with same name we have to check
441 // that it is the same case.
442 // indexof can return a table with different case letters.
445 if(relation
.RelationName
== this[tmp
].RelationName
)
446 throw new DuplicateNameException("A DataRelation named '" + relation
.RelationName
+ "' already belongs to this DataSet.");
452 /// Copies the elements of the specified DataRelation array to the end of the collection.
454 /// <param name="relations">The array of DataRelation objects to add to the collection.</param>
455 public virtual void AddRange(DataRelation
[] relations
)
457 foreach (DataRelation relation
in relations
) Add(relation
);
460 public virtual bool CanRemove(DataRelation relation
)
462 if (relation
== null || !GetDataSet().Equals(relation
.DataSet
))
465 // check if the relation doesnot belong to this collection
466 int tmp
= IndexOf(relation
.RelationName
);
467 // if we found a relation with same name we have to check
468 // that it is the same case.
469 // indexof can return a table with different case letters.
471 if(relation
.RelationName
!= this[tmp
].RelationName
)
481 public virtual void Clear()
486 public virtual bool Contains(string name
)
488 return (-1 != IndexOf (name
, false));
491 private CollectionChangeEventArgs
CreateCollectionChangeEvent (CollectionChangeAction action
)
493 return new CollectionChangeEventArgs (action
, this);
496 protected abstract DataSet
GetDataSet();
498 public virtual int IndexOf(DataRelation relation
)
500 return List
.IndexOf(relation
);
503 public virtual int IndexOf(string relationName
)
505 return IndexOf(relationName
, false);
508 private int IndexOf (string name
, bool error
)
510 int count
= 0, match
= -1;
511 for (int i
= 0; i
< List
.Count
; i
++)
513 String name2
= ((DataRelation
) List
[i
]).RelationName
;
514 if (String
.Compare (name
, name2
, true) == 0)
516 if (String
.Compare (name
, name2
, false) == 0)
524 if (count
> 1 && error
)
525 throw new ArgumentException ("There is no match for the name in the same case and there are multiple matches in different case.");
529 protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent
)
531 if (CollectionChanged
!= null)
532 CollectionChanged (this, ccevent
);
536 protected internal virtual void OnCollectionChanging (CollectionChangeEventArgs ccevent
)
538 throw new NotImplementedException ();
541 public void Remove (DataRelation relation
)
543 if (relation
== null)
544 throw new ArgumentNullException("Relation specified is null");
546 // check if the list doesnot contains this relation.
547 if (!(List
.Contains(relation
)))
548 throw new ArgumentException("Relation doesnot belong to this Collection.");
550 RemoveCore (relation
);
551 List
.Remove (relation
);
552 string name
= "Relation" + index
;
553 if (relation
.RelationName
== name
)
555 OnCollectionChanged (CreateCollectionChangeEvent (CollectionChangeAction
.Remove
));
558 public void Remove (string name
)
560 Remove ((DataRelation
) List
[IndexOf (name
)]);
563 public void RemoveAt (int index
)
565 if (( index
< 0 ) || (index
>=List
.Count
))
566 throw new ArgumentException("There is no row at position "+index
+".");
571 protected virtual void RemoveCore(DataRelation relation
)
573 // TODO: What have to be done?
578 [ResDescriptionAttribute ("Occurs whenever this collection's membership changes.")]
579 public event CollectionChangeEventHandler CollectionChanged
;