| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- //------------------------------------------------------------------------------
- // <copyright file="DataRelation.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- // <owner current="false" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- /*****************************************************************************************************
- 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) Enforce <rule 1> when
- a) Any FK value is changed
- b) A relation created that result in Multiple Nested Child
- WriteXml
- 1) WriteXml will throw if <rule 1> 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;
- /// <devdoc>
- /// <para>
- /// Represents a parent/child relationship between two tables.
- /// </para>
- /// </devdoc>
- [
- DefaultProperty("RelationName"),
- Editor("Microsoft.VSDesigner.Data.Design.DataRelationEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
- TypeConverter(typeof(RelationshipConverter)),
- ]
- public 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;
-
- /// <devdoc>
- /// this stores whether the child element appears beneath the parent in the XML persised files.
- /// </devdoc>
- internal bool nested = false;
- /// <devdoc>
- /// this stores whether the the relationship should make sure that KeyConstraints and ForeignKeyConstraints
- /// exist when added to the ConstraintsCollections of the table.
- /// </devdoc>
- internal bool createConstraints;
- private bool _checkMultipleNested = true;
- private static int _objectTypeCount; // Bid counter
- private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.DataRelation'/> class using the specified name,
- /// parent, and child columns.
- /// </para>
- /// </devdoc>
- public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn)
- : this(relationName, parentColumn, childColumn, true) {
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.DataRelation'/> class using the specified name, parent, and child columns, and
- /// value to create constraints.
- /// </para>
- /// </devdoc>
- public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn, bool createConstraints) {
- Bid.Trace("<ds.DataRelation.DataRelation|API> %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);
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.DataRelation'/> class using the specified name
- /// and matched arrays of parent and child columns.
- /// </para>
- /// </devdoc>
- public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns)
- : this(relationName, parentColumns, childColumns, true) {
- }
- /// <devdoc>
- /// <para>
- /// Initializes a new instance of the <see cref='System.Data.DataRelation'/> class using the specified name, matched arrays of parent
- /// and child columns, and value to create constraints.
- /// </para>
- /// </devdoc>
- public DataRelation(string relationName, DataColumn[] parentColumns, DataColumn[] childColumns, bool createConstraints) {
- Create(relationName, parentColumns, childColumns, createConstraints);
- }
- // Design time constructor
- /// <devdoc>
- /// <para>[To be supplied.]</para>
- /// </devdoc>
- [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;
- }
-
- /// <devdoc>
- /// <para>
- /// Gets the child columns of this relation.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.DataRelationChildColumnsDescr)
- ]
- public virtual DataColumn[] ChildColumns {
- get {
- CheckStateForProperty();
- return childKey.ToArray();
- }
- }
- internal DataColumn[] ChildColumnsReference {
- get {
- CheckStateForProperty();
- return childKey.ColumnsReference;
- }
- }
- /// <devdoc>
- /// The internal Key object for the child table.
- /// </devdoc>
- internal DataKey ChildKey {
- get {
- CheckStateForProperty();
- return childKey;
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the child table of this relation.
- /// </para>
- /// </devdoc>
- public virtual DataTable ChildTable {
- get {
- CheckStateForProperty();
- return childKey.Table;
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the <see cref='System.Data.DataSet'/> to which the relations' collection belongs to.
- /// </para>
- /// </devdoc>
- [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;
- }
- /// <devdoc>
- /// Gets the child rows for the parent row across the relation using the version given
- /// </devdoc>
- 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);
- }
- /// <devdoc>
- /// Gets the parent rows for the given child row across the relation using the version given
- /// </devdoc>
- 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)];
- }
- /// <devdoc>
- /// Internally sets the DataSet pointer.
- /// </devdoc>
- 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);
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the parent columns of this relation.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.DataRelationParentColumnsDescr)
- ]
- public virtual DataColumn[] ParentColumns {
- get {
- CheckStateForProperty();
- return parentKey.ToArray();
- }
- }
- internal DataColumn[] ParentColumnsReference {
- get {
- return parentKey.ColumnsReference;
- }
- }
- /// <devdoc>
- /// The internal constraint object for the parent table.
- /// </devdoc>
- internal DataKey ParentKey {
- get {
- CheckStateForProperty();
- return parentKey;
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the parent table of this relation.
- /// </para>
- /// </devdoc>
- public virtual DataTable ParentTable {
- get {
- CheckStateForProperty();
- return parentKey.Table;
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets or sets
- /// the name used to look up this relation in the parent
- /// data set's <see cref='System.Data.DataRelationCollection'/>.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- DefaultValue(""),
- ResDescriptionAttribute(Res.DataRelationRelationNameDescr)
- ]
- public virtual string RelationName {
- get {
- CheckStateForProperty();
- return relationName;
- }
- set {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<ds.DataRelation.set_RelationName|API> %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("<ds.DataRelation.CheckNestedRelations|INFO> %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<DataTable> list = new List<DataTable>();
- 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.
- ********************/
- /// <devdoc>
- /// <para>
- /// Gets or sets a value indicating whether relations are nested.
- /// </para>
- /// </devdoc>
- [
- ResCategoryAttribute(Res.DataCategory_Data),
- DefaultValue(false),
- ResDescriptionAttribute(Res.DataRelationNested)
- ]
- public virtual bool Nested {
- get {
- CheckStateForProperty();
- return nested;
- }
- set {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<ds.DataRelation.set_Nested|API> %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, null);
- } 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);
- }
- }
- }
- /// <devdoc>
- /// <para>
- /// Gets the constraint which ensures values in a column are unique.
- /// </para>
- /// </devdoc>
- 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;
- }
- /// <devdoc>
- /// <para>
- /// Gets the <see cref='System.Data.ForeignKeyConstraint'/> for the relation.
- /// </para>
- /// </devdoc>
- public virtual ForeignKeyConstraint ChildKeyConstraint {
- get {
- CheckStateForProperty();
- return childKeyConstraint;
- }
- }
- /// <devdoc>
- /// <para>Gets the collection of custom user information.</para>
- /// </devdoc>
- [
- 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();
- }
- }
- }
- /// <devdoc>
- /// <para>Checks to ensure the DataRelation is a valid object, even if it doesn't
- /// belong to a <see cref='System.Data.DataSet'/>.</para>
- /// </devdoc>
- 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, "<ds.DataRelation.Create|INFO> %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("<ds.DataRelation.Clone|INFO> %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("<ds.DataRelation.OnPropertyChanging|INFO> %d#\n", ObjectID);
- onPropertyChangingDelegate(this, pcevent);
- }
- }
-
- protected internal void RaisePropertyChanging(string name) {
- OnPropertyChanging(new PropertyChangedEventArgs(name));
- }
- /// <devdoc>
- /// </devdoc>
- 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;
- }
- }
- }
- }
|