| File: System\Data\UniqueConstraint.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Data { /// <summary> /// Represents a restriction on a set of columns in which all values must be unique. /// </summary> [DefaultProperty("ConstraintName")] [Editor("Microsoft.VSDesigner.Data.Design.UniqueConstraintEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public class UniqueConstraint : Constraint { private DataKey _key; private Index? _constraintIndex; internal bool _bPrimaryKey; // Design time serialization internal string? _constraintName; internal string[]? _columnNames; /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and /// <see cref='System.Data.DataColumn'/>. /// </summary> public UniqueConstraint(string? name, DataColumn column) { DataColumn[] columns = new DataColumn[1]; columns[0] = column; Create(name, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified <see cref='System.Data.DataColumn'/>. /// </summary> public UniqueConstraint(DataColumn column) { DataColumn[] columns = new DataColumn[1]; columns[0] = column; Create(null, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and array /// of <see cref='System.Data.DataColumn'/> objects. /// </summary> public UniqueConstraint(string? name, DataColumn[] columns) { Create(name, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the given array of <see cref='System.Data.DataColumn'/> /// objects. /// </summary> public UniqueConstraint(DataColumn[] columns) { Create(null, columns); } // Construct design time object [Browsable(false)] public UniqueConstraint(string? name, string[]? columnNames, bool isPrimaryKey) { _constraintName = name; _columnNames = columnNames; _bPrimaryKey = isPrimaryKey; } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and /// <see cref='System.Data.DataColumn'/>. /// </summary> public UniqueConstraint(string? name, DataColumn column, bool isPrimaryKey) { DataColumn[] columns = new DataColumn[1]; columns[0] = column; _bPrimaryKey = isPrimaryKey; Create(name, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified <see cref='System.Data.DataColumn'/>. /// </summary> public UniqueConstraint(DataColumn column, bool isPrimaryKey) { DataColumn[] columns = new DataColumn[1]; columns[0] = column; _bPrimaryKey = isPrimaryKey; Create(null, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and array /// of <see cref='System.Data.DataColumn'/> objects. /// </summary> public UniqueConstraint(string? name, DataColumn[] columns, bool isPrimaryKey) { _bPrimaryKey = isPrimaryKey; Create(name, columns); } /// <summary> /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the given array of <see cref='System.Data.DataColumn'/> /// objects. /// </summary> public UniqueConstraint(DataColumn[] columns, bool isPrimaryKey) { _bPrimaryKey = isPrimaryKey; Create(null, columns); } // design time serialization only internal string[] ColumnNames { get { return _key.GetColumnNames(); } } // Use constraint index only for search operations (and use key.GetSortIndex() when enumeration is needed and/or order is important) internal Index ConstraintIndex { get { AssertConstraintAndKeyIndexes(); return _constraintIndex!; } } [Conditional("DEBUG")] private void AssertConstraintAndKeyIndexes() { Debug.Assert(null != _constraintIndex, "null UniqueConstraint index"); // ideally, we would like constraintIndex and key.GetSortIndex to share the same index underneath: Debug.Assert(_constraintIndex == key.GetSortIndex) // but, there is a scenario where constraint and key indexes are built from the same list of columns but in a different order DataColumn[] sortIndexColumns = new DataColumn[_constraintIndex._indexFields.Length]; for (int i = 0; i < sortIndexColumns.Length; i++) { sortIndexColumns[i] = _constraintIndex._indexFields[i].Column; } Debug.Assert(DataKey.ColumnsEqual(_key.ColumnsReference, sortIndexColumns), "UniqueConstraint index columns do not match the key sort index"); } internal void ConstraintIndexClear() { if (null != _constraintIndex) { _constraintIndex.RemoveRef(); _constraintIndex = null; } } internal void ConstraintIndexInitialize() { if (null == _constraintIndex) { _constraintIndex = _key.GetSortIndex(); _constraintIndex.AddRef(); } AssertConstraintAndKeyIndexes(); } internal override void CheckState() { NonVirtualCheckState(); } private void NonVirtualCheckState() { _key.CheckState(); } internal override void CheckCanAddToCollection(ConstraintCollection constraints) { } internal override bool CanBeRemovedFromCollection(ConstraintCollection constraints, bool fThrowException) { if (Equals(constraints.Table._primaryKey)) { Debug.Assert(constraints.Table._primaryKey == this, "If the primary key and this are 'Equal', they should also be '=='"); if (!fThrowException) return false; else throw ExceptionBuilder.RemovePrimaryKey(constraints.Table); } for (ParentForeignKeyConstraintEnumerator cs = new ParentForeignKeyConstraintEnumerator(Table!.DataSet, Table); cs.GetNext();) { ForeignKeyConstraint constraint = cs.GetForeignKeyConstraint(); if (!_key.ColumnsEqual(constraint.ParentKey)) continue; if (!fThrowException) return false; else throw ExceptionBuilder.NeededForForeignKeyConstraint(this, constraint); } return true; } internal override bool CanEnableConstraint() { if (Table!.EnforceConstraints) return ConstraintIndex.CheckUnique(); return true; } internal override bool IsConstraintViolated() { bool result = false; Index index = ConstraintIndex; if (index.HasDuplicates) { object[] uniqueKeys = index.GetUniqueKeyValues(); for (int i = 0; i < uniqueKeys.Length; i++) { Range r = index.FindRecords((object[])uniqueKeys[i]); if (1 < r.Count) { DataRow[] rows = index.GetRows(r); string error = ExceptionBuilder.UniqueConstraintViolationText(_key.ColumnsReference, (object[])uniqueKeys[i]); for (int j = 0; j < rows.Length; j++) { rows[j].RowError = error; foreach (DataColumn dataColumn in _key.ColumnsReference) { rows[j].SetColumnError(dataColumn, error); } } result = true; } } } return result; } internal override void CheckConstraint(DataRow row, DataRowAction action) { if (Table!.EnforceConstraints && (action == DataRowAction.Add || action == DataRowAction.Change || (action == DataRowAction.Rollback && row._tempRecord != -1))) { if (row.HaveValuesChanged(ColumnsReference)) { if (ConstraintIndex.IsKeyRecordInIndex(row.GetDefaultRecord())) { object[] values = row.GetColumnValues(ColumnsReference); throw ExceptionBuilder.ConstraintViolation(ColumnsReference, values); } } } } internal override bool ContainsColumn(DataColumn column) { return _key.ContainsColumn(column); } internal override Constraint? Clone(DataSet destination) { return Clone(destination, false); } internal override Constraint? Clone(DataSet destination, bool ignorNSforTableLookup) { int iDest; if (ignorNSforTableLookup) { iDest = destination.Tables.IndexOf(Table!.TableName); } else { iDest = destination.Tables.IndexOf(Table!.TableName, Table.Namespace, false); // pass false for last param to be backward compatible } if (iDest < 0) return null; DataTable table = destination.Tables[iDest]; int keys = ColumnsReference.Length; DataColumn[] columns = new DataColumn[keys]; for (int i = 0; i < keys; i++) { DataColumn src = ColumnsReference[i]; iDest = table.Columns.IndexOf(src.ColumnName); if (iDest < 0) return null; columns[i] = table.Columns[iDest]; } UniqueConstraint clone = new UniqueConstraint(ConstraintName, columns); // ...Extended Properties foreach (object key in ExtendedProperties.Keys) { clone.ExtendedProperties[key] = ExtendedProperties[key]; } return clone; } internal UniqueConstraint? Clone(DataTable table) { int keys = ColumnsReference.Length; DataColumn[] columns = new DataColumn[keys]; for (int i = 0; i < keys; i++) { DataColumn src = ColumnsReference[i]; int iDest = table.Columns.IndexOf(src.ColumnName); if (iDest < 0) return null; columns[i] = table.Columns[iDest]; } UniqueConstraint clone = new UniqueConstraint(ConstraintName, columns); // ...Extended Properties foreach (object key in ExtendedProperties.Keys) { clone.ExtendedProperties[key] = ExtendedProperties[key]; } return clone; } /// <summary> /// Gets the array of columns that this constraint affects. /// </summary> [ReadOnly(true)] public virtual DataColumn[] Columns { get { return _key.ToArray(); } } internal DataColumn[] ColumnsReference { get { return _key.ColumnsReference; } } /// <summary> /// Gets a value indicating whether or not the constraint is on a primary key. /// </summary> public bool IsPrimaryKey { get { if (Table == null) { return false; } return (this == Table._primaryKey); } } private void Create(string? constraintName, DataColumn[] columns) { for (int i = 0; i < columns.Length; i++) { if (columns[i].Computed) { throw ExceptionBuilder.ExpressionInConstraint(columns[i]); } } _key = new DataKey(columns, true); ConstraintName = constraintName; NonVirtualCheckState(); } /// <summary> /// Compares this constraint to a second to determine if both are identical. /// </summary> public override bool Equals([NotNullWhen(true)] object? key2) { if (!(key2 is UniqueConstraint)) return false; return Key.ColumnsEqual(((UniqueConstraint)key2).Key); } public override int GetHashCode() { return base.GetHashCode(); } internal override bool InCollection { set { base.InCollection = value; if (_key.ColumnsReference.Length == 1) { _key.ColumnsReference[0].InternalUnique(value); } } } internal DataKey Key { get { return _key; } } /// <summary> /// Gets the table to which this constraint belongs. /// </summary> [ReadOnly(true)] public override DataTable? Table { get { if (_key.HasValue) { return _key.Table; } return null; } } // misc } }