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")]
50 [TypeConverterAttribute (typeof (RelationshipConverter))]
51 public class DataRelation {
52 private DataSet dataSet;
53 private string relationName;
54 private UniqueConstraint parentKeyConstraint;
55 private ForeignKeyConstraint childKeyConstraint;
56 private DataColumn [] parentColumns;
57 private DataColumn [] childColumns;
59 internal bool createConstraints = true;
60 private bool initFinished;
61 private PropertyCollection extendedProperties;
62 private PropertyChangedEventHandler onPropertyChangingDelegate;
65 string _parentTableName;
66 string _childTableName;
67 string [] _parentColumnNames;
68 string [] _childColumnNames;
70 bool initInProgress = false;
71 string _parentTableNameSpace = String.Empty;
72 string _childTableNameSpace = String.Empty;
76 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn)
77 : this (relationName, parentColumn, childColumn, true)
81 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns)
82 : this (relationName, parentColumns, childColumns, true)
86 public DataRelation (string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints)
87 : this (relationName, new DataColumn[] { parentColumn }, new DataColumn [] { childColumn }, createConstraints)
91 public DataRelation (string relationName, DataColumn [] parentColumns, DataColumn [] childColumns, bool createConstraints)
93 this.extendedProperties = new PropertyCollection ();
94 this.relationName = relationName == null ? string.Empty : relationName;
95 if (parentColumns == null)
96 throw new ArgumentNullException ("parentColumns");
97 this.parentColumns = parentColumns;
98 if (childColumns == null)
99 throw new ArgumentNullException ("childColumns");
100 this.childColumns = childColumns;
101 this.createConstraints = createConstraints;
102 if (parentColumns.Length != childColumns.Length)
103 throw new ArgumentException ("ParentColumns and ChildColumns should be the same length");
104 DataTable parentTable = parentColumns [0].Table;
105 DataTable childTable = childColumns [0].Table;
106 if (parentTable.DataSet != childTable.DataSet)
107 throw new InvalidConstraintException ();
108 foreach (DataColumn column in parentColumns)
109 if (column.Table != parentTable)
110 throw new InvalidConstraintException ();
111 foreach (DataColumn column in childColumns)
112 if (column.Table != childTable)
113 throw new InvalidConstraintException ();
115 for (int i = 0; i < ChildColumns.Length; i++)
116 if (!parentColumns [i].DataTypeMatches (childColumns [i]))
117 throw new InvalidConstraintException (
118 "Parent Columns and Child Columns don't have matching column types");
122 public DataRelation (string relationName, string parentTableName, string childTableName, string [] parentColumnNames, string[] childColumnNames, bool nested)
124 _relationName = relationName;
125 _parentTableName = parentTableName;
126 _childTableName = childTableName;
127 _parentColumnNames = parentColumnNames;
128 _childColumnNames = childColumnNames;
130 InitInProgress = true;
134 public DataRelation (string relationName, string parentTableName,
135 string parentTableNamespace, string childTableName,
136 string childTableNamespace, string[] parentColumnNames,
137 string[] childColumnNames, bool nested)
139 _relationName = relationName;
140 _parentTableName = parentTableName;
141 _parentTableNameSpace = parentTableNamespace;
142 _childTableName = childTableName;
143 _childTableNameSpace = childTableNamespace;
144 _parentColumnNames = parentColumnNames;
145 _childColumnNames = childColumnNames;
147 InitInProgress = true;
150 internal bool InitInProgress {
151 get { return initInProgress; }
152 set { initInProgress = value; }
155 internal void FinishInit (DataSet ds)
157 if (!ds.Tables.Contains (_parentTableName) || !ds.Tables.Contains (_childTableName))
158 throw new InvalidOperationException ();
160 if (_parentColumnNames.Length != _childColumnNames.Length)
161 throw new InvalidOperationException ();
163 DataTable parent = ds.Tables [_parentTableName];
164 DataTable child = ds.Tables [_childTableName];
166 parentColumns = new DataColumn [_parentColumnNames.Length];
167 childColumns = new DataColumn [_childColumnNames.Length];
169 for (int i = 0; i < _parentColumnNames.Length; ++i) {
170 if (!parent.Columns.Contains (_parentColumnNames [i]))
171 throw new InvalidOperationException ();
172 parentColumns [i] = parent.Columns [_parentColumnNames [i]];
173 if (!child.Columns.Contains (_childColumnNames [i]))
174 throw new InvalidOperationException ();
175 childColumns [i] = child.Columns [_childColumnNames [i]];
178 this.RelationName = _relationName;
179 this.Nested = _nested;
180 this.initFinished = true;
181 this.extendedProperties = new PropertyCollection ();
182 InitInProgress = false;
183 if (_parentTableNameSpace != String.Empty)
184 parent.Namespace = _parentTableNameSpace;
185 if (_childTableNameSpace != String.Empty)
186 child.Namespace = _childTableNameSpace;
189 #endregion // Constructors
193 [DataCategory ("Data")]
194 public virtual DataColumn [] ChildColumns {
195 get { return childColumns; }
198 public virtual ForeignKeyConstraint ChildKeyConstraint {
199 get { return childKeyConstraint; }
202 internal void SetChildKeyConstraint (ForeignKeyConstraint foreignKeyConstraint)
204 childKeyConstraint = foreignKeyConstraint;
207 public virtual DataTable ChildTable {
208 get { return childColumns [0].Table; }
212 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
213 public virtual DataSet DataSet {
214 get { return childColumns [0].Table.DataSet; }
218 [DataCategory ("Data")]
219 public PropertyCollection ExtendedProperties {
221 if (extendedProperties == null)
222 extendedProperties = new PropertyCollection();
223 return extendedProperties;
227 [DataCategory ("Data")]
228 [DefaultValue (false)]
229 public virtual bool Nested {
230 get { return nested; }
231 set { nested = value; }
234 [DataCategory ("Data")]
235 public virtual DataColumn[] ParentColumns {
236 get { return parentColumns; }
239 public virtual UniqueConstraint ParentKeyConstraint {
240 get { return parentKeyConstraint; }
243 internal void SetParentKeyConstraint (UniqueConstraint uniqueConstraint)
245 parentKeyConstraint = uniqueConstraint;
248 internal void SetDataSet (DataSet ds)
253 public virtual DataTable ParentTable {
254 get { return parentColumns [0].Table; }
257 [DataCategory ("Data")]
259 public virtual string RelationName {
260 get { return relationName; }
261 set { relationName = value; }
264 #endregion // Properties
268 protected void CheckStateForProperty ()
270 // TODO: check consistency of constraints
271 DataTable parentTable = parentColumns [0].Table;
272 DataTable childTable = childColumns [0].Table;
273 if (parentTable.DataSet != childTable.DataSet)
274 throw new DataException ();
275 bool allColumnsEqual = false;
276 for (int colCnt = 0; colCnt < parentColumns.Length; ++colCnt) {
277 if (!parentColumns [colCnt].DataType.Equals (childColumns [colCnt].DataType))
278 throw new DataException ();
279 if (parentColumns [colCnt] != childColumns [colCnt])
280 allColumnsEqual = false;
283 throw new DataException ();
286 protected internal void OnPropertyChanging (PropertyChangedEventArgs pcevent)
288 if (onPropertyChangingDelegate != null)
289 onPropertyChangingDelegate (this, pcevent);
292 protected internal void RaisePropertyChanging (string name)
294 OnPropertyChanging(new PropertyChangedEventArgs(name));
297 public override string ToString ()
302 internal void UpdateConstraints ()
304 if (initFinished || ! createConstraints)
307 ForeignKeyConstraint foreignKeyConstraint = null;
308 UniqueConstraint uniqueConstraint = null;
310 foreignKeyConstraint = FindForeignKey (ChildTable.Constraints);
311 uniqueConstraint = FindUniqueConstraint (ParentTable.Constraints);
313 // if we did not find the unique constraint in the parent table.
314 // we generate new uniqueConstraint and add it to the parent table.
315 if (uniqueConstraint == null) {
316 uniqueConstraint = new UniqueConstraint (ParentColumns, false);
317 ParentTable.Constraints.Add (uniqueConstraint);
320 // if we did not find the foreign key constraint in the parent table.
321 // we generate new foreignKeyConstraint and add it to the parent table.
322 if (foreignKeyConstraint == null) {
323 foreignKeyConstraint = new ForeignKeyConstraint (RelationName,
326 ChildTable.Constraints.Add (foreignKeyConstraint);
329 SetParentKeyConstraint (uniqueConstraint);
330 SetChildKeyConstraint (foreignKeyConstraint);
333 private static bool CompareDataColumns (DataColumn [] dc1, DataColumn [] dc2)
335 if (dc1.Length != dc2.Length)
338 for (int columnCnt = 0; columnCnt < dc1.Length; ++columnCnt){
339 if (dc1 [columnCnt] != dc2 [columnCnt])
345 private ForeignKeyConstraint FindForeignKey (ConstraintCollection cl)
347 ForeignKeyConstraint fkc = null;
348 foreach (Constraint o in cl) {
349 if (! (o is ForeignKeyConstraint))
351 fkc = (ForeignKeyConstraint) o;
352 /* Check ChildColumns & ParentColumns */
353 if (CompareDataColumns (ChildColumns, fkc.Columns) &&
354 CompareDataColumns (ParentColumns, fkc.RelatedColumns))
360 private UniqueConstraint FindUniqueConstraint (ConstraintCollection cl)
362 UniqueConstraint uc = null;
363 // find if the unique constraint already exists in the parent table.
364 foreach (Constraint o in cl){
365 if (! (o is UniqueConstraint))
367 uc = (UniqueConstraint) o;
368 //Check in ParentColumns
369 if (CompareDataColumns (ParentColumns, uc.Columns))
376 /// Check whether the given column is part of this relation.
379 /// true if the column is part of this relation, otherwise false.
381 internal bool Contains (DataColumn column)
383 foreach (DataColumn col in ParentColumns)
387 foreach (DataColumn col in ChildColumns)
393 #endregion // Methods