2 // System.Data.DataRelation.cs
5 // Daniel Morgan <danmorg@sc.rr.com>
6 // Alan Tam Siu Lung <Tam@SiuLung.com>
7 // Tim Coleman <tim@timcoleman.com>
9 // (C) 2002 Daniel Morgan
10 // (C) 2002 Ximian, Inc.
11 // Copyright (C) Tim Coleman, 2003
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.ComponentModel;
39 using System.Runtime.Serialization;
44 /// DataRelation is used for a parent/child relationship
45 /// between two DataTable objects
47 [Editor ("Microsoft.VSDesigner.Data.Design.DataRelationEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
48 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
49 [DefaultProperty ("RelationName")]
53 [TypeConverterAttribute (typeof (RelationshipConverter))]
54 public class DataRelation {
55 private DataSet dataSet;
56 private string relationName;
57 private UniqueConstraint parentKeyConstraint;
58 private ForeignKeyConstraint childKeyConstraint;
59 private DataColumn [] parentColumns;
60 private DataColumn [] childColumns;
62 internal bool createConstraints = true;
63 private bool initFinished;
64 private PropertyCollection extendedProperties;
65 private PropertyChangedEventHandler onPropertyChangingDelegate;
68 string _parentTableName;
69 string _childTableName;
70 string [] _parentColumnNames;
71 string [] _childColumnNames;
73 bool initInProgress = false;
75 string _parentTableNameSpace = String.Empty;
76 string _childTableNameSpace = String.Empty;
81 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn)
82 : this (relationName, parentColumn, childColumn, true)
86 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns)
87 : this (relationName, parentColumns, childColumns, true)
91 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
92 : this (relationName, new DataColumn[] { parentColumn }, new DataColumn [] { childColumn }, createConstraints)
96 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns, bool createConstraints)
98 this.extendedProperties = new PropertyCollection ();
99 this.relationName = relationName == null ? string.Empty : relationName;
100 if (parentColumns == null)
101 throw new ArgumentNullException ("parentColumns");
102 this.parentColumns = parentColumns;
103 if (childColumns == null)
104 throw new ArgumentNullException ("childColumns");
105 this.childColumns = childColumns;
106 this.createConstraints = createConstraints;
107 if (parentColumns.Length != childColumns.Length)
108 throw new ArgumentException ("ParentColumns and ChildColumns should be the same length");
109 DataTable parentTable = parentColumns [0].Table;
110 DataTable childTable = childColumns [0].Table;
111 if (parentTable.DataSet != childTable.DataSet)
112 throw new InvalidConstraintException ();
113 foreach (DataColumn column in parentColumns)
114 if (column.Table != parentTable)
115 throw new InvalidConstraintException ();
116 foreach (DataColumn column in childColumns)
117 if (column.Table != childTable)
118 throw new InvalidConstraintException ();
120 for (int i = 0; i < ChildColumns.Length; i++)
121 if (!parentColumns [i].DataTypeMatches (childColumns [i]))
122 throw new InvalidConstraintException (
123 "Parent Columns and Child Columns don't have matching column types");
127 public DataRelation (string relationName, string parentTableName, string childTableName, string [] parentColumnNames, string[] childColumnNames, bool nested)
129 _relationName = relationName;
130 _parentTableName = parentTableName;
131 _childTableName = childTableName;
132 _parentColumnNames = parentColumnNames;
133 _childColumnNames = childColumnNames;
135 InitInProgress = true;
140 public DataRelation (string relationName, string parentTableName,
141 string parentTableNameSpace, string childTableName,
142 string childTableNameSpace, string[] parentColumnNames,
143 string[] childColumnNames, bool nested)
145 _relationName = relationName;
146 _parentTableName = parentTableName;
147 _parentTableNameSpace = parentTableNameSpace;
148 _childTableName = childTableName;
149 _childTableNameSpace = childTableNameSpace;
150 _parentColumnNames = parentColumnNames;
151 _childColumnNames = childColumnNames;
153 InitInProgress = true;
157 internal bool InitInProgress {
158 get { return initInProgress; }
159 set { initInProgress = value; }
162 internal void FinishInit (DataSet ds)
164 if (!ds.Tables.Contains (_parentTableName) || !ds.Tables.Contains (_childTableName))
165 throw new InvalidOperationException ();
167 if (_parentColumnNames.Length != _childColumnNames.Length)
168 throw new InvalidOperationException ();
170 DataTable parent = ds.Tables [_parentTableName];
171 DataTable child = ds.Tables [_childTableName];
173 parentColumns = new DataColumn [_parentColumnNames.Length];
174 childColumns = new DataColumn [_childColumnNames.Length];
176 for (int i = 0; i < _parentColumnNames.Length; ++i) {
177 if (!parent.Columns.Contains (_parentColumnNames [i]))
178 throw new InvalidOperationException ();
179 parentColumns [i] = parent.Columns [_parentColumnNames [i]];
180 if (!child.Columns.Contains (_childColumnNames [i]))
181 throw new InvalidOperationException ();
182 childColumns [i] = child.Columns [_childColumnNames [i]];
185 this.RelationName = _relationName;
186 this.Nested = _nested;
187 this.initFinished = true;
188 this.extendedProperties = new PropertyCollection ();
189 InitInProgress = false;
191 if (_parentTableNameSpace != String.Empty)
192 parent.Namespace = _parentTableNameSpace;
193 if (_childTableNameSpace != String.Empty)
194 child.Namespace = _childTableNameSpace;
198 #endregion // Constructors
202 [DataCategory ("Data")]
204 [DataSysDescription ("Indicates the child columns of this relation.")]
206 public virtual DataColumn [] ChildColumns {
207 get { return childColumns; }
210 public virtual ForeignKeyConstraint ChildKeyConstraint {
211 get { return childKeyConstraint; }
214 internal void SetChildKeyConstraint (ForeignKeyConstraint foreignKeyConstraint)
216 childKeyConstraint = foreignKeyConstraint;
219 public virtual DataTable ChildTable {
220 get { return childColumns [0].Table; }
224 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
225 public virtual DataSet DataSet {
226 get { return childColumns [0].Table.DataSet; }
230 [DataCategory ("Data")]
232 [DataSysDescription ("The collection that holds custom user information.")]
234 public PropertyCollection ExtendedProperties {
236 if (extendedProperties == null)
237 extendedProperties = new PropertyCollection();
238 return extendedProperties;
242 [DataCategory ("Data")]
244 [DataSysDescription ("Indicates whether relations are nested.")]
246 [DefaultValue (false)]
247 public virtual bool Nested {
248 get { return nested; }
249 set { nested = value; }
252 [DataCategory ("Data")]
254 [DataSysDescription ("Indicates the parent columns of this relation.")]
256 public virtual DataColumn[] ParentColumns {
257 get { return parentColumns; }
260 public virtual UniqueConstraint ParentKeyConstraint {
261 get { return parentKeyConstraint; }
264 internal void SetParentKeyConstraint (UniqueConstraint uniqueConstraint)
266 parentKeyConstraint = uniqueConstraint;
269 internal void SetDataSet (DataSet ds)
274 public virtual DataTable ParentTable {
275 get { return parentColumns [0].Table; }
278 [DataCategory ("Data")]
280 [DataSysDescription ("The name used to look up this relation in the Relations collection of a DataSet.")]
283 public virtual string RelationName {
284 get { return relationName; }
285 set { relationName = value; }
288 #endregion // Properties
292 protected void CheckStateForProperty ()
294 // TODO: check consistency of constraints
295 DataTable parentTable = parentColumns [0].Table;
296 DataTable childTable = childColumns [0].Table;
297 if (parentTable.DataSet != childTable.DataSet)
298 throw new DataException ();
299 bool allColumnsEqual = false;
300 for (int colCnt = 0; colCnt < parentColumns.Length; ++colCnt) {
301 if (!parentColumns [colCnt].DataType.Equals (childColumns [colCnt].DataType))
302 throw new DataException ();
303 if (parentColumns [colCnt] != childColumns [colCnt])
304 allColumnsEqual = false;
307 throw new DataException ();
310 protected internal void OnPropertyChanging (PropertyChangedEventArgs pcevent)
312 if (onPropertyChangingDelegate != null)
313 onPropertyChangingDelegate (this, pcevent);
316 protected internal void RaisePropertyChanging (string name)
318 OnPropertyChanging(new PropertyChangedEventArgs(name));
321 public override string ToString ()
326 internal void UpdateConstraints ()
328 if (initFinished || ! createConstraints)
331 ForeignKeyConstraint foreignKeyConstraint = null;
332 UniqueConstraint uniqueConstraint = null;
334 foreignKeyConstraint = FindForeignKey (ChildTable.Constraints);
335 uniqueConstraint = FindUniqueConstraint (ParentTable.Constraints);
337 // if we did not find the unique constraint in the parent table.
338 // we generate new uniqueConstraint and add it to the parent table.
339 if (uniqueConstraint == null) {
340 uniqueConstraint = new UniqueConstraint (ParentColumns, false);
341 ParentTable.Constraints.Add (uniqueConstraint);
344 // if we did not find the foreign key constraint in the parent table.
345 // we generate new foreignKeyConstraint and add it to the parent table.
346 if (foreignKeyConstraint == null) {
347 foreignKeyConstraint = new ForeignKeyConstraint (RelationName,
350 ChildTable.Constraints.Add (foreignKeyConstraint);
353 SetParentKeyConstraint (uniqueConstraint);
354 SetChildKeyConstraint (foreignKeyConstraint);
357 private static bool CompareDataColumns (DataColumn [] dc1, DataColumn [] dc2)
359 if (dc1.Length != dc2.Length)
362 for (int columnCnt = 0; columnCnt < dc1.Length; ++columnCnt){
363 if (dc1 [columnCnt] != dc2 [columnCnt])
369 private ForeignKeyConstraint FindForeignKey (ConstraintCollection cl)
371 ForeignKeyConstraint fkc = null;
372 foreach (Constraint o in cl) {
373 if (! (o is ForeignKeyConstraint))
375 fkc = (ForeignKeyConstraint) o;
376 /* Check ChildColumns & ParentColumns */
377 if (CompareDataColumns (ChildColumns, fkc.Columns) &&
378 CompareDataColumns (ParentColumns, fkc.RelatedColumns))
384 private UniqueConstraint FindUniqueConstraint (ConstraintCollection cl)
386 UniqueConstraint uc = null;
387 // find if the unique constraint already exists in the parent table.
388 foreach (Constraint o in cl){
389 if (! (o is UniqueConstraint))
391 uc = (UniqueConstraint) o;
392 //Check in ParentColumns
393 if (CompareDataColumns (ParentColumns, uc.Columns))
400 /// Check whether the given column is part of this relation.
403 /// true if the column is part of this relation, otherwise false.
405 internal bool Contains (DataColumn column)
407 foreach (DataColumn col in ParentColumns)
411 foreach (DataColumn col in ChildColumns)
417 #endregion // Methods