Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / Data / System / Data / DataRelation.cs / 1 / DataRelation.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //[....] //----------------------------------------------------------------------------- /***************************************************************************************************** Rules for Multiple Nested Parent, enforce following constraints 1) At all times, only 1(ONE) FK can be NON-Null in a row. 2) NULL FK values are not associated with PARENT(x), even if if PK is NULL in Parent 3) Enforcewhen a) Any FK value is changed b) A relation created that result in Multiple Nested Child WriteXml 1) WriteXml will throw if is violated 2) if NON-Null FK has parentRow (boolean check) print as Nested, else it will get written as normal row additional notes: We decided to enforce the rule 1 just if Xml being persisted ******************************************************************************************************/ namespace System.Data { using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Data.Common; using System.Collections.Generic; /// /// [ DefaultProperty("RelationName"), Editor("Microsoft.VSDesigner.Data.Design.DataRelationEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), TypeConverter(typeof(RelationshipConverter)), ] #if WINFSInternalOnly internal #else public #endif class DataRelation { // properties private DataSet dataSet = null; internal PropertyCollection extendedProperties = null; internal string relationName = ""; // events private PropertyChangedEventHandler onPropertyChangingDelegate = null; // state private DataKey childKey; private DataKey parentKey; private UniqueConstraint parentKeyConstraint = null; private ForeignKeyConstraint childKeyConstraint = null; // Design time serialization internal string[] parentColumnNames = null; internal string[] childColumnNames = null; internal string parentTableName = null; internal string childTableName = null; internal string parentTableNamespace= null; internal string childTableNamespace = null; ////// Represents a parent/child relationship between two tables. /// ////// this stores whether the child element appears beneath the parent in the XML persised files. /// internal bool nested = false; ////// this stores whether the the relationship should make sure that KeyConstraints and ForeignKeyConstraints /// exist when added to the ConstraintsCollections of the table. /// internal bool createConstraints; private bool _checkMultipleNested = true; private static int _objectTypeCount; // Bid counter private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); ////// public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn) : this(relationName, parentColumn, childColumn, true) { } ////// Initializes a new instance of the ///class using the specified name, /// parent, and child columns. /// /// public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints) { Bid.Trace("/// Initializes a new instance of the ///class using the specified name, parent, and child columns, and /// value to create constraints. /// %d#, relationName='%ls', parentColumn=%d, childColumn=%d, createConstraints=%d{bool}\n", ObjectID, relationName, (parentColumn != null) ? parentColumn.ObjectID : 0, (childColumn != null) ? childColumn.ObjectID : 0, createConstraints); DataColumn[] parentColumns = new DataColumn[1]; parentColumns[0] = parentColumn; DataColumn[] childColumns = new DataColumn[1]; childColumns[0] = childColumn; Create(relationName, parentColumns, childColumns, createConstraints); } /// /// public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns) : this(relationName, parentColumns, childColumns, true) { } ////// Initializes a new instance of the ///class using the specified name /// and matched arrays of parent and child columns. /// /// public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) { Create(relationName, parentColumns, childColumns, createConstraints); } // Design time constructor ////// Initializes a new instance of the ///class using the specified name, matched arrays of parent /// and child columns, and value to create constraints. /// /// [Browsable(false)] public DataRelation(string relationName, string parentTableName, string childTableName, string[] parentColumnNames, string[] childColumnNames, bool nested) { this.relationName = relationName; this.parentColumnNames = parentColumnNames; this.childColumnNames = childColumnNames; this.parentTableName = parentTableName; this.childTableName = childTableName; this.nested = nested; // DataRelation(relationName, parentTableName, null, childTableName, null, parentColumnNames, childColumnNames, nested) } [Browsable(false)] // Design time constructor public DataRelation(string relationName, string parentTableName, string parentTableNamespace, string childTableName, string childTableNamespace, string[] parentColumnNames, string[] childColumnNames, bool nested) { this.relationName = relationName; this.parentColumnNames = parentColumnNames; this.childColumnNames = childColumnNames; this.parentTableName = parentTableName; this.childTableName = childTableName; this.parentTableNamespace = parentTableNamespace; this.childTableNamespace = childTableNamespace; this.nested = nested; } ///[To be supplied.] ////// [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataRelationChildColumnsDescr) ] public virtual DataColumn[] ChildColumns { get { CheckStateForProperty(); return childKey.ToArray(); } } internal DataColumn[] ChildColumnsReference { get { CheckStateForProperty(); return childKey.ColumnsReference; } } ////// Gets the child columns of this relation. /// ////// The internal Key object for the child table. /// internal DataKey ChildKey { get { CheckStateForProperty(); return childKey; } } ////// public virtual DataTable ChildTable { get { CheckStateForProperty(); return childKey.Table; } } ////// Gets the child table of this relation. /// ////// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)] public virtual DataSet DataSet { get { CheckStateForProperty(); return dataSet; } } internal string[] ParentColumnNames { get { return parentKey.GetColumnNames(); } } internal string[] ChildColumnNames { get { return childKey.GetColumnNames(); } } private static bool IsKeyNull(object[] values) { for (int i = 0; i < values.Length; i++) { if (!DataStorage.IsObjectNull(values[i])) return false; } return true; } ////// Gets the ///to which the relations' collection belongs to. /// /// Gets the child rows for the parent row across the relation using the version given /// internal static DataRow[] GetChildRows(DataKey parentKey, DataKey childKey, DataRow parentRow, DataRowVersion version) { object[] values = parentRow.GetKeyValues(parentKey, version); if (IsKeyNull(values)) { return childKey.Table.NewRowArray(0); } Index index = childKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); return index.GetRows(values); } ////// Gets the parent rows for the given child row across the relation using the version given /// internal static DataRow[] GetParentRows(DataKey parentKey, DataKey childKey, DataRow childRow, DataRowVersion version) { object[] values = childRow.GetKeyValues(childKey, version); if (IsKeyNull(values)) { return parentKey.Table.NewRowArray(0); } Index index = parentKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); return index.GetRows(values); } internal static DataRow GetParentRow(DataKey parentKey, DataKey childKey, DataRow childRow, DataRowVersion version) { if (!childRow.HasVersion((version == DataRowVersion.Original) ? DataRowVersion.Original : DataRowVersion.Current)) if (childRow.tempRecord == -1) return null; object[] values = childRow.GetKeyValues(childKey, version); if (IsKeyNull(values)) { return null; } Index index = parentKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); Range range = index.FindRecords(values); if (range.IsNull) { return null; } if (range.Count > 1) { throw ExceptionBuilder.MultipleParents(); } return parentKey.Table.recordManager[index.GetRecord(range.Min)]; } ////// Internally sets the DataSet pointer. /// internal void SetDataSet(DataSet dataSet) { if (this.dataSet != dataSet) { this.dataSet = dataSet; } } internal void SetParentRowRecords(DataRow childRow, DataRow parentRow) { object[] parentKeyValues = parentRow.GetKeyValues(ParentKey); if (childRow.tempRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.tempRecord, ChildKey, parentKeyValues); } if (childRow.newRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.newRecord, ChildKey, parentKeyValues); } if (childRow.oldRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.oldRecord, ChildKey, parentKeyValues); } } ////// [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataRelationParentColumnsDescr) ] public virtual DataColumn[] ParentColumns { get { CheckStateForProperty(); return parentKey.ToArray(); } } internal DataColumn[] ParentColumnsReference { get { return parentKey.ColumnsReference; } } ////// Gets the parent columns of this relation. /// ////// The internal constraint object for the parent table. /// internal DataKey ParentKey { get { CheckStateForProperty(); return parentKey; } } ////// public virtual DataTable ParentTable { get { CheckStateForProperty(); return parentKey.Table; } } ////// Gets the parent table of this relation. /// ////// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(""), ResDescriptionAttribute(Res.DataRelationRelationNameDescr) ] public virtual string RelationName { get { CheckStateForProperty(); return relationName; } set { IntPtr hscp; Bid.ScopeEnter(out hscp, "/// Gets or sets /// the name used to look up this relation in the parent /// data set's ///. /// %d#, '%ls'\n", ObjectID, value); try { if (value == null) value = ""; CultureInfo locale = (dataSet != null ? dataSet.Locale : CultureInfo.CurrentCulture); if (String.Compare(relationName, value, true, locale) != 0) { if (dataSet != null) { if (value.Length == 0) throw ExceptionBuilder.NoRelationName(); dataSet.Relations.RegisterName(value); if (relationName.Length != 0) dataSet.Relations.UnregisterName(relationName); } this.relationName = value; ((DataRelationCollection.DataTableRelationCollection)(ParentTable.ChildRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); ((DataRelationCollection.DataTableRelationCollection)(ChildTable.ParentRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); } else if (String.Compare(relationName, value, false, locale) != 0) { relationName = value; ((DataRelationCollection.DataTableRelationCollection)(ParentTable.ChildRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); ((DataRelationCollection.DataTableRelationCollection)(ChildTable.ParentRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); } } finally{ Bid.ScopeLeave(ref hscp); } } } internal void CheckNamespaceValidityForNestedRelations(string ns) { foreach(DataRelation rel in ChildTable.ParentRelations) { if (rel == this || rel.Nested) { if (rel.ParentTable.Namespace != ns) { throw ExceptionBuilder.InValidNestedRelation(ChildTable.TableName); } } } } internal void CheckNestedRelations() { Bid.Trace(" %d#\n", ObjectID); Debug.Assert(DataSet == null || ! nested, "this relation supposed to be not in dataset or not nested"); // 1. There is no other relation (R) that has this.ChildTable as R.ChildTable // This is not valid for Whidbey anymore so the code has been removed // 2. There is no loop in nested relations #if DEBUG int numTables = ParentTable.DataSet.Tables.Count; #endif DataTable dt = ParentTable; if (ChildTable == ParentTable){ if (String.Compare(ChildTable.TableName, ChildTable.DataSet.DataSetName, true, ChildTable.DataSet.Locale) == 0) throw ExceptionBuilder.SelfnestedDatasetConflictingName(ChildTable.TableName); return; //allow self join tables. } List list = new List (); list.Add(ChildTable); // We have already checked for nested relaion UP for(int i = 0; i < list.Count; ++i) { DataRelation[] relations = list[i].NestedParentRelations; foreach(DataRelation rel in relations) { if (rel.ParentTable == ChildTable && rel.ChildTable != ChildTable) { throw ExceptionBuilder.LoopInNestedRelations(ChildTable.TableName); } if (!list.Contains (rel.ParentTable)) { // check for self nested list.Add(rel.ParentTable); } } } } /******************** The Namespace of a table nested inside multiple parents can be 1. Explicitly specified 2. Inherited from Parent Table 3. Empty (Form = unqualified case) However, Schema does not allow (3) to be a global element and multiple nested child has to be a global element. Therefore we'll reduce case (3) to (2) if all parents have same namespace else throw. ********************/ /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(false), ResDescriptionAttribute(Res.DataRelationNested) ] public virtual bool Nested { get { CheckStateForProperty(); return nested; } set { IntPtr hscp; Bid.ScopeEnter(out hscp, "/// Gets or sets a value indicating whether relations are nested. /// ///%d#, %d{bool}\n", ObjectID, value); try { if (nested != value) { if (dataSet != null) { if (value) { if (ChildTable.IsNamespaceInherited()) { // if not added to collection, don't do this check CheckNamespaceValidityForNestedRelations(ParentTable.Namespace); } Debug.Assert(ChildTable != null, "On a DataSet, but not on Table. Bad state"); ForeignKeyConstraint constraint = ChildTable.Constraints.FindForeignKeyConstraint(ChildKey.ColumnsReference, ParentKey.ColumnsReference); if (constraint != null) { constraint.CheckConstraint(); } ValidateMultipleNestedRelations(); } } if (!value && (parentKey.ColumnsReference[0].ColumnMapping == MappingType.Hidden)) throw ExceptionBuilder.RelationNestedReadOnly(); if (value) { this.ParentTable.Columns.RegisterColumnName(this.ChildTable.TableName, (DataColumn)null, this.ChildTable); } else { this.ParentTable.Columns.UnregisterName(this.ChildTable.TableName); } RaisePropertyChanging("Nested"); if(value) { CheckNestedRelations(); if (this.DataSet != null) if (ParentTable == ChildTable) { foreach(DataRow row in ChildTable.Rows) row.CheckForLoops(this); if (ChildTable.DataSet != null && ( String.Compare(ChildTable.TableName, ChildTable.DataSet.DataSetName, true, ChildTable.DataSet.Locale) == 0) ) throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName); ChildTable.fNestedInDataset = false; } else { foreach(DataRow row in ChildTable.Rows) row.GetParentRow(this); } this.ParentTable.ElementColumnCount++; } else { this.ParentTable.ElementColumnCount--; } this.nested = value; ChildTable.CacheNestedParent(); if (value) { if (ADP.IsEmpty(ChildTable.Namespace) && ((ChildTable.NestedParentsCount > 1) || ((ChildTable.NestedParentsCount > 0) && ! (ChildTable.DataSet.Relations.Contains(this.RelationName))))) { string parentNs = null; foreach(DataRelation rel in ChildTable.ParentRelations) { if (rel.Nested) { if (null == parentNs) { parentNs = rel.ParentTable.Namespace; } else { if (String.Compare(parentNs, rel.ParentTable.Namespace, StringComparison.Ordinal) != 0) { this.nested = false; throw ExceptionBuilder.InvalidParentNamespaceinNestedRelation(ChildTable.TableName); } } } } // if not already in memory , form == unqualified if (CheckMultipleNested && ChildTable.tableNamespace != null && ChildTable.tableNamespace.Length == 0) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } ChildTable.tableNamespace = null; // if we dont throw, then let it inherit the Namespace } } } } finally{ Bid.ScopeLeave(ref hscp); } } } /// /// public virtual UniqueConstraint ParentKeyConstraint { get { CheckStateForProperty(); return parentKeyConstraint; } } internal void SetParentKeyConstraint(UniqueConstraint value) { Debug.Assert(parentKeyConstraint == null || value == null, "ParentKeyConstraint should not have been set already."); parentKeyConstraint = value; } ////// Gets the constraint which ensures values in a column are unique. /// ////// public virtual ForeignKeyConstraint ChildKeyConstraint { get { CheckStateForProperty(); return childKeyConstraint; } } ////// Gets the ///for the relation. /// /// [ ResCategoryAttribute(Res.DataCategory_Data), Browsable(false), ResDescriptionAttribute(Res.ExtendedPropertiesDescr) ] public PropertyCollection ExtendedProperties { get { if (extendedProperties == null) { extendedProperties = new PropertyCollection(); } return extendedProperties; } } internal bool CheckMultipleNested { get { return _checkMultipleNested; } set { _checkMultipleNested = value; } } internal void SetChildKeyConstraint(ForeignKeyConstraint value) { Debug.Assert(childKeyConstraint == null || value == null, "ChildKeyConstraint should not have been set already."); childKeyConstraint = value; } internal event PropertyChangedEventHandler PropertyChanging { add { onPropertyChangingDelegate += value; } remove { onPropertyChangingDelegate -= value; } } // If we're not in a dataSet relations collection, we need to verify on every property get that we're // still a good relation object. internal void CheckState() { if (dataSet == null) { parentKey.CheckState(); childKey.CheckState(); if (parentKey.Table.DataSet != childKey.Table.DataSet) { throw ExceptionBuilder.RelationDataSetMismatch(); } if (childKey.ColumnsEqual(parentKey)) { throw ExceptionBuilder.KeyColumnsIdentical(); } for (int i = 0; i < parentKey.ColumnsReference.Length; i++) { if ((parentKey.ColumnsReference[i].DataType != childKey.ColumnsReference[i].DataType) || ((parentKey.ColumnsReference[i].DataType == typeof(DateTime)) && (parentKey.ColumnsReference[i].DateTimeMode != childKey.ColumnsReference[i].DateTimeMode) && ((parentKey.ColumnsReference[i].DateTimeMode & childKey.ColumnsReference[i].DateTimeMode) != DataSetDateTime.Unspecified))) // alow unspecified and unspecifiedlocal throw ExceptionBuilder.ColumnsTypeMismatch(); } } } ///Gets the collection of custom user information. ////// protected void CheckStateForProperty() { try { CheckState(); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { throw ExceptionBuilder.BadObjectPropertyAccess(e.Message); } throw; } } private void Create(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) { IntPtr hscp; Bid.ScopeEnter(out hscp, "Checks to ensure the DataRelation is a valid object, even if it doesn't /// belong to a ///. %d#, relationName='%ls', createConstraints=%d{bool}\n", ObjectID, relationName, createConstraints); try { this.parentKey = new DataKey(parentColumns, true); this.childKey = new DataKey(childColumns, true); if (parentColumns.Length != childColumns.Length) throw ExceptionBuilder.KeyLengthMismatch(); for(int i = 0; i < parentColumns.Length; i++){ if ((parentColumns[i].Table.DataSet == null) || (childColumns[i].Table.DataSet == null)) throw ExceptionBuilder.ParentOrChildColumnsDoNotHaveDataSet(); } CheckState(); this.relationName = (relationName == null ? "" : relationName); this.createConstraints = createConstraints; } finally{ Bid.ScopeLeave(ref hscp); } } internal DataRelation Clone(DataSet destination) { Bid.Trace(" %d#, destination=%d\n", ObjectID, (destination != null) ? destination.ObjectID : 0); DataTable parent = destination.Tables[ParentTable.TableName, ParentTable.Namespace]; DataTable child = destination.Tables[ChildTable.TableName, ChildTable.Namespace]; int keyLength = parentKey.ColumnsReference.Length; DataColumn[] parentColumns = new DataColumn[keyLength]; DataColumn[] childColumns = new DataColumn[keyLength]; for (int i = 0; i < keyLength; i++) { parentColumns[i] = parent.Columns[ParentKey.ColumnsReference[i].ColumnName]; childColumns[i] = child.Columns[ChildKey.ColumnsReference[i].ColumnName]; } DataRelation clone = new DataRelation(relationName, parentColumns, childColumns, false); clone.CheckMultipleNested = false; // disable the check in clone as it is already created clone.Nested = this.Nested; clone.CheckMultipleNested = true; // enable the check // ...Extended Properties if (this.extendedProperties != null) { foreach(Object key in this.extendedProperties.Keys) { clone.ExtendedProperties[key]=this.extendedProperties[key]; } } return clone; } protected internal void OnPropertyChanging(PropertyChangedEventArgs pcevent) { if (onPropertyChangingDelegate != null) { Bid.Trace(" %d#\n", ObjectID); onPropertyChangingDelegate(this, pcevent); } } protected internal void RaisePropertyChanging(string name) { OnPropertyChanging(new PropertyChangedEventArgs(name)); } /// /// public override string ToString() { return RelationName; } internal void ValidateMultipleNestedRelations() { // find all nested relations that this child table has // if this relation is the only relation it has, then fine, // otherwise check if all relations are created from XSD, without using Key/KeyRef // check all keys to see autogenerated if (!this.Nested || !CheckMultipleNested) // no need for this verification return; if (0 < ChildTable.NestedParentRelations.Length) { DataColumn[] childCols = ChildColumns; if (childCols.Length != 1 || !IsAutoGenerated(childCols[0])) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } if (!XmlTreeGen.AutoGenerated(this)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } foreach (Constraint cs in ChildTable.Constraints) { if (cs is ForeignKeyConstraint) { ForeignKeyConstraint fk = (ForeignKeyConstraint) cs; if (!XmlTreeGen.AutoGenerated(fk, true)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } } else { UniqueConstraint unique = (UniqueConstraint) cs; if (!XmlTreeGen.AutoGenerated(unique)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } } } } } private bool IsAutoGenerated(DataColumn col) { if (col.ColumnMapping != MappingType.Hidden) return false; if (col.DataType != typeof(int)) return false; string generatedname = col.Table.TableName+"_Id"; if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname+"_0")) return true; generatedname = this.ParentColumnsReference[0].Table.TableName+"_Id"; if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname+"_0")) return true; return false; } internal int ObjectID { get { return _objectID; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //[....] //----------------------------------------------------------------------------- /***************************************************************************************************** Rules for Multiple Nested Parent, enforce following constraints 1) At all times, only 1(ONE) FK can be NON-Null in a row. 2) NULL FK values are not associated with PARENT(x), even if if PK is NULL in Parent 3) Enforcewhen a) Any FK value is changed b) A relation created that result in Multiple Nested Child WriteXml 1) WriteXml will throw if is violated 2) if NON-Null FK has parentRow (boolean check) print as Nested, else it will get written as normal row additional notes: We decided to enforce the rule 1 just if Xml being persisted ******************************************************************************************************/ namespace System.Data { using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Data.Common; using System.Collections.Generic; /// /// [ DefaultProperty("RelationName"), Editor("Microsoft.VSDesigner.Data.Design.DataRelationEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), TypeConverter(typeof(RelationshipConverter)), ] #if WINFSInternalOnly internal #else public #endif class DataRelation { // properties private DataSet dataSet = null; internal PropertyCollection extendedProperties = null; internal string relationName = ""; // events private PropertyChangedEventHandler onPropertyChangingDelegate = null; // state private DataKey childKey; private DataKey parentKey; private UniqueConstraint parentKeyConstraint = null; private ForeignKeyConstraint childKeyConstraint = null; // Design time serialization internal string[] parentColumnNames = null; internal string[] childColumnNames = null; internal string parentTableName = null; internal string childTableName = null; internal string parentTableNamespace= null; internal string childTableNamespace = null; ////// Represents a parent/child relationship between two tables. /// ////// this stores whether the child element appears beneath the parent in the XML persised files. /// internal bool nested = false; ////// this stores whether the the relationship should make sure that KeyConstraints and ForeignKeyConstraints /// exist when added to the ConstraintsCollections of the table. /// internal bool createConstraints; private bool _checkMultipleNested = true; private static int _objectTypeCount; // Bid counter private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); ////// public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn) : this(relationName, parentColumn, childColumn, true) { } ////// Initializes a new instance of the ///class using the specified name, /// parent, and child columns. /// /// public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints) { Bid.Trace("/// Initializes a new instance of the ///class using the specified name, parent, and child columns, and /// value to create constraints. /// %d#, relationName='%ls', parentColumn=%d, childColumn=%d, createConstraints=%d{bool}\n", ObjectID, relationName, (parentColumn != null) ? parentColumn.ObjectID : 0, (childColumn != null) ? childColumn.ObjectID : 0, createConstraints); DataColumn[] parentColumns = new DataColumn[1]; parentColumns[0] = parentColumn; DataColumn[] childColumns = new DataColumn[1]; childColumns[0] = childColumn; Create(relationName, parentColumns, childColumns, createConstraints); } /// /// public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns) : this(relationName, parentColumns, childColumns, true) { } ////// Initializes a new instance of the ///class using the specified name /// and matched arrays of parent and child columns. /// /// public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) { Create(relationName, parentColumns, childColumns, createConstraints); } // Design time constructor ////// Initializes a new instance of the ///class using the specified name, matched arrays of parent /// and child columns, and value to create constraints. /// /// [Browsable(false)] public DataRelation(string relationName, string parentTableName, string childTableName, string[] parentColumnNames, string[] childColumnNames, bool nested) { this.relationName = relationName; this.parentColumnNames = parentColumnNames; this.childColumnNames = childColumnNames; this.parentTableName = parentTableName; this.childTableName = childTableName; this.nested = nested; // DataRelation(relationName, parentTableName, null, childTableName, null, parentColumnNames, childColumnNames, nested) } [Browsable(false)] // Design time constructor public DataRelation(string relationName, string parentTableName, string parentTableNamespace, string childTableName, string childTableNamespace, string[] parentColumnNames, string[] childColumnNames, bool nested) { this.relationName = relationName; this.parentColumnNames = parentColumnNames; this.childColumnNames = childColumnNames; this.parentTableName = parentTableName; this.childTableName = childTableName; this.parentTableNamespace = parentTableNamespace; this.childTableNamespace = childTableNamespace; this.nested = nested; } ///[To be supplied.] ////// [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataRelationChildColumnsDescr) ] public virtual DataColumn[] ChildColumns { get { CheckStateForProperty(); return childKey.ToArray(); } } internal DataColumn[] ChildColumnsReference { get { CheckStateForProperty(); return childKey.ColumnsReference; } } ////// Gets the child columns of this relation. /// ////// The internal Key object for the child table. /// internal DataKey ChildKey { get { CheckStateForProperty(); return childKey; } } ////// public virtual DataTable ChildTable { get { CheckStateForProperty(); return childKey.Table; } } ////// Gets the child table of this relation. /// ////// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)] public virtual DataSet DataSet { get { CheckStateForProperty(); return dataSet; } } internal string[] ParentColumnNames { get { return parentKey.GetColumnNames(); } } internal string[] ChildColumnNames { get { return childKey.GetColumnNames(); } } private static bool IsKeyNull(object[] values) { for (int i = 0; i < values.Length; i++) { if (!DataStorage.IsObjectNull(values[i])) return false; } return true; } ////// Gets the ///to which the relations' collection belongs to. /// /// Gets the child rows for the parent row across the relation using the version given /// internal static DataRow[] GetChildRows(DataKey parentKey, DataKey childKey, DataRow parentRow, DataRowVersion version) { object[] values = parentRow.GetKeyValues(parentKey, version); if (IsKeyNull(values)) { return childKey.Table.NewRowArray(0); } Index index = childKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); return index.GetRows(values); } ////// Gets the parent rows for the given child row across the relation using the version given /// internal static DataRow[] GetParentRows(DataKey parentKey, DataKey childKey, DataRow childRow, DataRowVersion version) { object[] values = childRow.GetKeyValues(childKey, version); if (IsKeyNull(values)) { return parentKey.Table.NewRowArray(0); } Index index = parentKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); return index.GetRows(values); } internal static DataRow GetParentRow(DataKey parentKey, DataKey childKey, DataRow childRow, DataRowVersion version) { if (!childRow.HasVersion((version == DataRowVersion.Original) ? DataRowVersion.Original : DataRowVersion.Current)) if (childRow.tempRecord == -1) return null; object[] values = childRow.GetKeyValues(childKey, version); if (IsKeyNull(values)) { return null; } Index index = parentKey.GetSortIndex((version == DataRowVersion.Original) ? DataViewRowState.OriginalRows : DataViewRowState.CurrentRows); Range range = index.FindRecords(values); if (range.IsNull) { return null; } if (range.Count > 1) { throw ExceptionBuilder.MultipleParents(); } return parentKey.Table.recordManager[index.GetRecord(range.Min)]; } ////// Internally sets the DataSet pointer. /// internal void SetDataSet(DataSet dataSet) { if (this.dataSet != dataSet) { this.dataSet = dataSet; } } internal void SetParentRowRecords(DataRow childRow, DataRow parentRow) { object[] parentKeyValues = parentRow.GetKeyValues(ParentKey); if (childRow.tempRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.tempRecord, ChildKey, parentKeyValues); } if (childRow.newRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.newRecord, ChildKey, parentKeyValues); } if (childRow.oldRecord != -1) { ChildTable.recordManager.SetKeyValues(childRow.oldRecord, ChildKey, parentKeyValues); } } ////// [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataRelationParentColumnsDescr) ] public virtual DataColumn[] ParentColumns { get { CheckStateForProperty(); return parentKey.ToArray(); } } internal DataColumn[] ParentColumnsReference { get { return parentKey.ColumnsReference; } } ////// Gets the parent columns of this relation. /// ////// The internal constraint object for the parent table. /// internal DataKey ParentKey { get { CheckStateForProperty(); return parentKey; } } ////// public virtual DataTable ParentTable { get { CheckStateForProperty(); return parentKey.Table; } } ////// Gets the parent table of this relation. /// ////// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(""), ResDescriptionAttribute(Res.DataRelationRelationNameDescr) ] public virtual string RelationName { get { CheckStateForProperty(); return relationName; } set { IntPtr hscp; Bid.ScopeEnter(out hscp, "/// Gets or sets /// the name used to look up this relation in the parent /// data set's ///. /// %d#, '%ls'\n", ObjectID, value); try { if (value == null) value = ""; CultureInfo locale = (dataSet != null ? dataSet.Locale : CultureInfo.CurrentCulture); if (String.Compare(relationName, value, true, locale) != 0) { if (dataSet != null) { if (value.Length == 0) throw ExceptionBuilder.NoRelationName(); dataSet.Relations.RegisterName(value); if (relationName.Length != 0) dataSet.Relations.UnregisterName(relationName); } this.relationName = value; ((DataRelationCollection.DataTableRelationCollection)(ParentTable.ChildRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); ((DataRelationCollection.DataTableRelationCollection)(ChildTable.ParentRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); } else if (String.Compare(relationName, value, false, locale) != 0) { relationName = value; ((DataRelationCollection.DataTableRelationCollection)(ParentTable.ChildRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); ((DataRelationCollection.DataTableRelationCollection)(ChildTable.ParentRelations)).OnRelationPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this)); } } finally{ Bid.ScopeLeave(ref hscp); } } } internal void CheckNamespaceValidityForNestedRelations(string ns) { foreach(DataRelation rel in ChildTable.ParentRelations) { if (rel == this || rel.Nested) { if (rel.ParentTable.Namespace != ns) { throw ExceptionBuilder.InValidNestedRelation(ChildTable.TableName); } } } } internal void CheckNestedRelations() { Bid.Trace(" %d#\n", ObjectID); Debug.Assert(DataSet == null || ! nested, "this relation supposed to be not in dataset or not nested"); // 1. There is no other relation (R) that has this.ChildTable as R.ChildTable // This is not valid for Whidbey anymore so the code has been removed // 2. There is no loop in nested relations #if DEBUG int numTables = ParentTable.DataSet.Tables.Count; #endif DataTable dt = ParentTable; if (ChildTable == ParentTable){ if (String.Compare(ChildTable.TableName, ChildTable.DataSet.DataSetName, true, ChildTable.DataSet.Locale) == 0) throw ExceptionBuilder.SelfnestedDatasetConflictingName(ChildTable.TableName); return; //allow self join tables. } List list = new List (); list.Add(ChildTable); // We have already checked for nested relaion UP for(int i = 0; i < list.Count; ++i) { DataRelation[] relations = list[i].NestedParentRelations; foreach(DataRelation rel in relations) { if (rel.ParentTable == ChildTable && rel.ChildTable != ChildTable) { throw ExceptionBuilder.LoopInNestedRelations(ChildTable.TableName); } if (!list.Contains (rel.ParentTable)) { // check for self nested list.Add(rel.ParentTable); } } } } /******************** The Namespace of a table nested inside multiple parents can be 1. Explicitly specified 2. Inherited from Parent Table 3. Empty (Form = unqualified case) However, Schema does not allow (3) to be a global element and multiple nested child has to be a global element. Therefore we'll reduce case (3) to (2) if all parents have same namespace else throw. ********************/ /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(false), ResDescriptionAttribute(Res.DataRelationNested) ] public virtual bool Nested { get { CheckStateForProperty(); return nested; } set { IntPtr hscp; Bid.ScopeEnter(out hscp, "/// Gets or sets a value indicating whether relations are nested. /// ///%d#, %d{bool}\n", ObjectID, value); try { if (nested != value) { if (dataSet != null) { if (value) { if (ChildTable.IsNamespaceInherited()) { // if not added to collection, don't do this check CheckNamespaceValidityForNestedRelations(ParentTable.Namespace); } Debug.Assert(ChildTable != null, "On a DataSet, but not on Table. Bad state"); ForeignKeyConstraint constraint = ChildTable.Constraints.FindForeignKeyConstraint(ChildKey.ColumnsReference, ParentKey.ColumnsReference); if (constraint != null) { constraint.CheckConstraint(); } ValidateMultipleNestedRelations(); } } if (!value && (parentKey.ColumnsReference[0].ColumnMapping == MappingType.Hidden)) throw ExceptionBuilder.RelationNestedReadOnly(); if (value) { this.ParentTable.Columns.RegisterColumnName(this.ChildTable.TableName, (DataColumn)null, this.ChildTable); } else { this.ParentTable.Columns.UnregisterName(this.ChildTable.TableName); } RaisePropertyChanging("Nested"); if(value) { CheckNestedRelations(); if (this.DataSet != null) if (ParentTable == ChildTable) { foreach(DataRow row in ChildTable.Rows) row.CheckForLoops(this); if (ChildTable.DataSet != null && ( String.Compare(ChildTable.TableName, ChildTable.DataSet.DataSetName, true, ChildTable.DataSet.Locale) == 0) ) throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName); ChildTable.fNestedInDataset = false; } else { foreach(DataRow row in ChildTable.Rows) row.GetParentRow(this); } this.ParentTable.ElementColumnCount++; } else { this.ParentTable.ElementColumnCount--; } this.nested = value; ChildTable.CacheNestedParent(); if (value) { if (ADP.IsEmpty(ChildTable.Namespace) && ((ChildTable.NestedParentsCount > 1) || ((ChildTable.NestedParentsCount > 0) && ! (ChildTable.DataSet.Relations.Contains(this.RelationName))))) { string parentNs = null; foreach(DataRelation rel in ChildTable.ParentRelations) { if (rel.Nested) { if (null == parentNs) { parentNs = rel.ParentTable.Namespace; } else { if (String.Compare(parentNs, rel.ParentTable.Namespace, StringComparison.Ordinal) != 0) { this.nested = false; throw ExceptionBuilder.InvalidParentNamespaceinNestedRelation(ChildTable.TableName); } } } } // if not already in memory , form == unqualified if (CheckMultipleNested && ChildTable.tableNamespace != null && ChildTable.tableNamespace.Length == 0) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } ChildTable.tableNamespace = null; // if we dont throw, then let it inherit the Namespace } } } } finally{ Bid.ScopeLeave(ref hscp); } } } /// /// public virtual UniqueConstraint ParentKeyConstraint { get { CheckStateForProperty(); return parentKeyConstraint; } } internal void SetParentKeyConstraint(UniqueConstraint value) { Debug.Assert(parentKeyConstraint == null || value == null, "ParentKeyConstraint should not have been set already."); parentKeyConstraint = value; } ////// Gets the constraint which ensures values in a column are unique. /// ////// public virtual ForeignKeyConstraint ChildKeyConstraint { get { CheckStateForProperty(); return childKeyConstraint; } } ////// Gets the ///for the relation. /// /// [ ResCategoryAttribute(Res.DataCategory_Data), Browsable(false), ResDescriptionAttribute(Res.ExtendedPropertiesDescr) ] public PropertyCollection ExtendedProperties { get { if (extendedProperties == null) { extendedProperties = new PropertyCollection(); } return extendedProperties; } } internal bool CheckMultipleNested { get { return _checkMultipleNested; } set { _checkMultipleNested = value; } } internal void SetChildKeyConstraint(ForeignKeyConstraint value) { Debug.Assert(childKeyConstraint == null || value == null, "ChildKeyConstraint should not have been set already."); childKeyConstraint = value; } internal event PropertyChangedEventHandler PropertyChanging { add { onPropertyChangingDelegate += value; } remove { onPropertyChangingDelegate -= value; } } // If we're not in a dataSet relations collection, we need to verify on every property get that we're // still a good relation object. internal void CheckState() { if (dataSet == null) { parentKey.CheckState(); childKey.CheckState(); if (parentKey.Table.DataSet != childKey.Table.DataSet) { throw ExceptionBuilder.RelationDataSetMismatch(); } if (childKey.ColumnsEqual(parentKey)) { throw ExceptionBuilder.KeyColumnsIdentical(); } for (int i = 0; i < parentKey.ColumnsReference.Length; i++) { if ((parentKey.ColumnsReference[i].DataType != childKey.ColumnsReference[i].DataType) || ((parentKey.ColumnsReference[i].DataType == typeof(DateTime)) && (parentKey.ColumnsReference[i].DateTimeMode != childKey.ColumnsReference[i].DateTimeMode) && ((parentKey.ColumnsReference[i].DateTimeMode & childKey.ColumnsReference[i].DateTimeMode) != DataSetDateTime.Unspecified))) // alow unspecified and unspecifiedlocal throw ExceptionBuilder.ColumnsTypeMismatch(); } } } ///Gets the collection of custom user information. ////// protected void CheckStateForProperty() { try { CheckState(); } catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { throw ExceptionBuilder.BadObjectPropertyAccess(e.Message); } throw; } } private void Create(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) { IntPtr hscp; Bid.ScopeEnter(out hscp, "Checks to ensure the DataRelation is a valid object, even if it doesn't /// belong to a ///. %d#, relationName='%ls', createConstraints=%d{bool}\n", ObjectID, relationName, createConstraints); try { this.parentKey = new DataKey(parentColumns, true); this.childKey = new DataKey(childColumns, true); if (parentColumns.Length != childColumns.Length) throw ExceptionBuilder.KeyLengthMismatch(); for(int i = 0; i < parentColumns.Length; i++){ if ((parentColumns[i].Table.DataSet == null) || (childColumns[i].Table.DataSet == null)) throw ExceptionBuilder.ParentOrChildColumnsDoNotHaveDataSet(); } CheckState(); this.relationName = (relationName == null ? "" : relationName); this.createConstraints = createConstraints; } finally{ Bid.ScopeLeave(ref hscp); } } internal DataRelation Clone(DataSet destination) { Bid.Trace(" %d#, destination=%d\n", ObjectID, (destination != null) ? destination.ObjectID : 0); DataTable parent = destination.Tables[ParentTable.TableName, ParentTable.Namespace]; DataTable child = destination.Tables[ChildTable.TableName, ChildTable.Namespace]; int keyLength = parentKey.ColumnsReference.Length; DataColumn[] parentColumns = new DataColumn[keyLength]; DataColumn[] childColumns = new DataColumn[keyLength]; for (int i = 0; i < keyLength; i++) { parentColumns[i] = parent.Columns[ParentKey.ColumnsReference[i].ColumnName]; childColumns[i] = child.Columns[ChildKey.ColumnsReference[i].ColumnName]; } DataRelation clone = new DataRelation(relationName, parentColumns, childColumns, false); clone.CheckMultipleNested = false; // disable the check in clone as it is already created clone.Nested = this.Nested; clone.CheckMultipleNested = true; // enable the check // ...Extended Properties if (this.extendedProperties != null) { foreach(Object key in this.extendedProperties.Keys) { clone.ExtendedProperties[key]=this.extendedProperties[key]; } } return clone; } protected internal void OnPropertyChanging(PropertyChangedEventArgs pcevent) { if (onPropertyChangingDelegate != null) { Bid.Trace(" %d#\n", ObjectID); onPropertyChangingDelegate(this, pcevent); } } protected internal void RaisePropertyChanging(string name) { OnPropertyChanging(new PropertyChangedEventArgs(name)); } /// /// public override string ToString() { return RelationName; } internal void ValidateMultipleNestedRelations() { // find all nested relations that this child table has // if this relation is the only relation it has, then fine, // otherwise check if all relations are created from XSD, without using Key/KeyRef // check all keys to see autogenerated if (!this.Nested || !CheckMultipleNested) // no need for this verification return; if (0 < ChildTable.NestedParentRelations.Length) { DataColumn[] childCols = ChildColumns; if (childCols.Length != 1 || !IsAutoGenerated(childCols[0])) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } if (!XmlTreeGen.AutoGenerated(this)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } foreach (Constraint cs in ChildTable.Constraints) { if (cs is ForeignKeyConstraint) { ForeignKeyConstraint fk = (ForeignKeyConstraint) cs; if (!XmlTreeGen.AutoGenerated(fk, true)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } } else { UniqueConstraint unique = (UniqueConstraint) cs; if (!XmlTreeGen.AutoGenerated(unique)) { throw ExceptionBuilder.TableCantBeNestedInTwoTables(ChildTable.TableName); } } } } } private bool IsAutoGenerated(DataColumn col) { if (col.ColumnMapping != MappingType.Hidden) return false; if (col.DataType != typeof(int)) return false; string generatedname = col.Table.TableName+"_Id"; if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname+"_0")) return true; generatedname = this.ParentColumnsReference[0].Table.TableName+"_Id"; if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname+"_0")) return true; return false; } internal int ObjectID { get { return _objectID; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SqlUtil.cs
- XmlNamespaceDeclarationsAttribute.cs
- SR.cs
- TableLayoutRowStyleCollection.cs
- RectIndependentAnimationStorage.cs
- ExtensionFile.cs
- ActiveXSite.cs
- XamlReader.cs
- FixedPage.cs
- MetadataExporter.cs
- ServerIdentity.cs
- COM2ExtendedUITypeEditor.cs
- SynchronizedInputProviderWrapper.cs
- MethodCallExpression.cs
- DmlSqlGenerator.cs
- GenericXmlSecurityTokenAuthenticator.cs
- SmiEventSink_DeferedProcessing.cs
- MediaTimeline.cs
- SAPIEngineTypes.cs
- ObjectKeyFrameCollection.cs
- Attribute.cs
- TreeViewAutomationPeer.cs
- FixedStringLookup.cs
- TableLayoutSettings.cs
- ToolStripInSituService.cs
- StringAnimationUsingKeyFrames.cs
- ListSourceHelper.cs
- DoubleCollectionValueSerializer.cs
- LoadMessageLogger.cs
- ToolStripControlHost.cs
- SpecialNameAttribute.cs
- RunClient.cs
- SqlProcedureAttribute.cs
- ValidateNames.cs
- TreeBuilderXamlTranslator.cs
- DiagnosticStrings.cs
- ColumnPropertiesGroup.cs
- CompositeClientFormatter.cs
- Soap12ProtocolImporter.cs
- PaginationProgressEventArgs.cs
- COM2Enum.cs
- WebPartChrome.cs
- XmlNamedNodeMap.cs
- PassportAuthentication.cs
- SynchronizedDispatch.cs
- RequiredFieldValidator.cs
- CatalogPartCollection.cs
- ContentFileHelper.cs
- HotCommands.cs
- DateTimeConstantAttribute.cs
- CodeSubDirectory.cs
- Vector3DCollection.cs
- AssemblyInfo.cs
- TypeReference.cs
- CodeVariableReferenceExpression.cs
- RsaKeyIdentifierClause.cs
- SHA512.cs
- Sequence.cs
- BuildManagerHost.cs
- RequestResponse.cs
- DllNotFoundException.cs
- LogPolicy.cs
- safelink.cs
- DateTimeFormat.cs
- WebPartEditVerb.cs
- EncryptedKey.cs
- SecurityIdentifierElement.cs
- Activity.cs
- XmlUtilWriter.cs
- PartialArray.cs
- TextProperties.cs
- WebServiceHostFactory.cs
- IsolatedStorage.cs
- SessionPageStatePersister.cs
- ControlUtil.cs
- WizardPanelChangingEventArgs.cs
- securestring.cs
- CallInfo.cs
- LayoutEvent.cs
- DataBoundControlHelper.cs
- ImageDrawing.cs
- TextParentUndoUnit.cs
- SynchronizedInputProviderWrapper.cs
- VirtualizedContainerService.cs
- HtmlInputImage.cs
- WebHttpBinding.cs
- CommandTreeTypeHelper.cs
- TimeStampChecker.cs
- Padding.cs
- PropertyEmitterBase.cs
- QilVisitor.cs
- EventDescriptorCollection.cs
- ExternalException.cs
- ModelVisual3D.cs
- AutoSizeComboBox.cs
- OracleCommandSet.cs
- ProjectionCamera.cs
- NumericUpDown.cs
- Int32Collection.cs
- MenuItemBinding.cs