UniqueConstraint.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. //------------------------------------------------------------------------------
  2. // <copyright file="UniqueConstraint.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. // <owner current="false" primary="false">[....]</owner>
  8. //------------------------------------------------------------------------------
  9. namespace System.Data {
  10. using System;
  11. using System.Diagnostics;
  12. using System.ComponentModel;
  13. /// <devdoc>
  14. /// <para>
  15. /// Represents a restriction on a set of columns in which all values must be unique.
  16. /// </para>
  17. /// </devdoc>
  18. [
  19. DefaultProperty("ConstraintName"),
  20. Editor("Microsoft.VSDesigner.Data.Design.UniqueConstraintEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
  21. ]
  22. public class UniqueConstraint : Constraint {
  23. private DataKey key;
  24. private Index _constraintIndex;
  25. internal bool bPrimaryKey = false;
  26. // Design time serialization
  27. internal string constraintName = null;
  28. internal string[] columnNames = null;
  29. /// <devdoc>
  30. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and
  31. /// <see cref='System.Data.DataColumn'/>.</para>
  32. /// </devdoc>
  33. public UniqueConstraint(String name, DataColumn column) {
  34. DataColumn[] columns = new DataColumn[1];
  35. columns[0] = column;
  36. Create(name, columns);
  37. }
  38. /// <devdoc>
  39. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified <see cref='System.Data.DataColumn'/>.</para>
  40. /// </devdoc>
  41. public UniqueConstraint(DataColumn column) {
  42. DataColumn[] columns = new DataColumn[1];
  43. columns[0] = column;
  44. Create(null, columns);
  45. }
  46. /// <devdoc>
  47. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and array
  48. /// of <see cref='System.Data.DataColumn'/> objects.</para>
  49. /// </devdoc>
  50. public UniqueConstraint(String name, DataColumn[] columns) {
  51. Create(name, columns);
  52. }
  53. /// <devdoc>
  54. /// <para>
  55. /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the given array of <see cref='System.Data.DataColumn'/>
  56. /// objects.
  57. /// </para>
  58. /// </devdoc>
  59. public UniqueConstraint(DataColumn[] columns) {
  60. Create(null, columns);
  61. }
  62. // Construct design time object
  63. /// <devdoc>
  64. /// <para>[To be supplied.]</para>
  65. /// </devdoc>
  66. [Browsable(false)]
  67. public UniqueConstraint(String name, string[] columnNames, bool isPrimaryKey) {
  68. this.constraintName = name;
  69. this.columnNames = columnNames;
  70. this.bPrimaryKey = isPrimaryKey;
  71. }
  72. /// <devdoc>
  73. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and
  74. /// <see cref='System.Data.DataColumn'/>.</para>
  75. /// </devdoc>
  76. public UniqueConstraint(String name, DataColumn column, bool isPrimaryKey) {
  77. DataColumn[] columns = new DataColumn[1];
  78. columns[0] = column;
  79. this.bPrimaryKey = isPrimaryKey;
  80. Create(name, columns);
  81. }
  82. /// <devdoc>
  83. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified <see cref='System.Data.DataColumn'/>.</para>
  84. /// </devdoc>
  85. public UniqueConstraint(DataColumn column, bool isPrimaryKey) {
  86. DataColumn[] columns = new DataColumn[1];
  87. columns[0] = column;
  88. this.bPrimaryKey = isPrimaryKey;
  89. Create(null, columns);
  90. }
  91. /// <devdoc>
  92. /// <para>Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the specified name and array
  93. /// of <see cref='System.Data.DataColumn'/> objects.</para>
  94. /// </devdoc>
  95. public UniqueConstraint(String name, DataColumn[] columns, bool isPrimaryKey) {
  96. this.bPrimaryKey = isPrimaryKey;
  97. Create(name, columns);
  98. }
  99. /// <devdoc>
  100. /// <para>
  101. /// Initializes a new instance of the <see cref='System.Data.UniqueConstraint'/> with the given array of <see cref='System.Data.DataColumn'/>
  102. /// objects.
  103. /// </para>
  104. /// </devdoc>
  105. public UniqueConstraint(DataColumn[] columns, bool isPrimaryKey) {
  106. this.bPrimaryKey = isPrimaryKey;
  107. Create(null, columns);
  108. }
  109. // design time serialization only
  110. internal string[] ColumnNames {
  111. get {
  112. return key.GetColumnNames();
  113. }
  114. }
  115. // VSTFDEVDIV 895693: please note that there are scenarios where ConstraintIndex is not the same as key.GetSortIndex()
  116. // Use constraint index only for search operations (and use key.GetSortIndex() when enumeration is needed and/or order is important)
  117. internal Index ConstraintIndex {
  118. get {
  119. AssertConstraintAndKeyIndexes();
  120. return _constraintIndex;
  121. }
  122. }
  123. [Conditional("DEBUG")]
  124. private void AssertConstraintAndKeyIndexes() {
  125. Debug.Assert(null != _constraintIndex, "null UniqueConstraint index");
  126. // ideally, we would like constraintIndex and key.GetSortIndex to share the same index underneath: Debug.Assert(_constraintIndex == key.GetSortIndex)
  127. // but, due to VSTFDEVDIV #895693 there is a scenario where constraint and key indexes are built from the same list of columns but in a different order
  128. DataColumn[] sortIndexColumns = new DataColumn[_constraintIndex.IndexFields.Length];
  129. for (int i = 0; i < sortIndexColumns.Length; i++) {
  130. sortIndexColumns[i] = _constraintIndex.IndexFields[i].Column;
  131. }
  132. Debug.Assert(DataKey.ColumnsEqual(key.ColumnsReference, sortIndexColumns), "UniqueConstraint index columns do not match the key sort index");
  133. }
  134. internal void ConstraintIndexClear() {
  135. if (null != _constraintIndex) {
  136. _constraintIndex.RemoveRef();
  137. _constraintIndex = null;
  138. }
  139. }
  140. internal void ConstraintIndexInitialize() {
  141. //Debug.Assert(null == _constraintIndex, "non-null UniqueConstraint index");
  142. if (null == _constraintIndex) {
  143. _constraintIndex = key.GetSortIndex();
  144. _constraintIndex.AddRef();
  145. }
  146. AssertConstraintAndKeyIndexes();
  147. }
  148. internal override void CheckState() {
  149. NonVirtualCheckState();
  150. }
  151. private void NonVirtualCheckState() {
  152. key.CheckState();
  153. }
  154. internal override void CheckCanAddToCollection(ConstraintCollection constraints) {
  155. }
  156. internal override bool CanBeRemovedFromCollection(ConstraintCollection constraints, bool fThrowException) {
  157. if (this.Equals(constraints.Table.primaryKey)) {
  158. Debug.Assert(constraints.Table.primaryKey == this, "If the primary key and this are 'Equal', they should also be '=='");
  159. if (!fThrowException)
  160. return false;
  161. else
  162. throw ExceptionBuilder.RemovePrimaryKey(constraints.Table);
  163. }
  164. for (ParentForeignKeyConstraintEnumerator cs = new ParentForeignKeyConstraintEnumerator(Table.DataSet, Table); cs.GetNext();) {
  165. ForeignKeyConstraint constraint = cs.GetForeignKeyConstraint();
  166. if (!key.ColumnsEqual(constraint.ParentKey))
  167. continue;
  168. if (!fThrowException)
  169. return false;
  170. else
  171. throw ExceptionBuilder.NeededForForeignKeyConstraint(this, constraint);
  172. }
  173. return true;
  174. }
  175. internal override bool CanEnableConstraint() {
  176. if (Table.EnforceConstraints)
  177. return ConstraintIndex.CheckUnique();
  178. return true;
  179. }
  180. internal override bool IsConstraintViolated() {
  181. bool result = false;
  182. Index index = ConstraintIndex;
  183. if (index.HasDuplicates) {
  184. //
  185. object[] uniqueKeys = index.GetUniqueKeyValues();
  186. for (int i = 0; i < uniqueKeys.Length; i++) {
  187. Range r = index.FindRecords((object[])uniqueKeys[i]);
  188. if (1 < r.Count) {
  189. DataRow[] rows = index.GetRows(r);
  190. string error = ExceptionBuilder.UniqueConstraintViolationText(key.ColumnsReference, (object[])uniqueKeys[i]);
  191. for (int j = 0; j < rows.Length; j++) {
  192. //
  193. rows[j].RowError = error;
  194. foreach(DataColumn dataColumn in key.ColumnsReference){
  195. rows[j].SetColumnError(dataColumn, error);
  196. }
  197. }
  198. // SQLBU 20011224: set_RowError for all DataRow with a unique constraint violation
  199. result = true;
  200. }
  201. }
  202. }
  203. return result;
  204. }
  205. internal override void CheckConstraint(DataRow row, DataRowAction action) {
  206. if (Table.EnforceConstraints &&
  207. (action == DataRowAction.Add ||
  208. action == DataRowAction.Change ||
  209. (action == DataRowAction.Rollback && row.tempRecord != -1))) {
  210. if (row.HaveValuesChanged(ColumnsReference)) {
  211. if (ConstraintIndex.IsKeyRecordInIndex(row.GetDefaultRecord())) {
  212. object[] values = row.GetColumnValues(ColumnsReference);
  213. throw ExceptionBuilder.ConstraintViolation(ColumnsReference, values);
  214. }
  215. }
  216. }
  217. }
  218. internal override bool ContainsColumn(DataColumn column) {
  219. return key.ContainsColumn(column);
  220. }
  221. internal override Constraint Clone(DataSet destination) {
  222. return Clone(destination, false);
  223. }
  224. internal override Constraint Clone(DataSet destination, bool ignorNSforTableLookup) {
  225. int iDest;
  226. if (ignorNSforTableLookup) {
  227. iDest = destination.Tables.IndexOf(Table.TableName);
  228. }
  229. else {
  230. iDest = destination.Tables.IndexOf(Table.TableName, Table.Namespace, false);// pass false for last param to be backward compatable
  231. }
  232. if (iDest < 0)
  233. return null;
  234. DataTable table = destination.Tables[iDest];
  235. int keys = ColumnsReference.Length;
  236. DataColumn[] columns = new DataColumn[keys];
  237. for (int i = 0; i < keys; i++) {
  238. DataColumn src = ColumnsReference[i];
  239. iDest = table.Columns.IndexOf(src.ColumnName);
  240. if (iDest < 0)
  241. return null;
  242. columns[i] = table.Columns[iDest];
  243. }
  244. UniqueConstraint clone = new UniqueConstraint(ConstraintName, columns);
  245. // ...Extended Properties
  246. foreach(Object key in this.ExtendedProperties.Keys) {
  247. clone.ExtendedProperties[key]=this.ExtendedProperties[key];
  248. }
  249. return clone;
  250. }
  251. internal UniqueConstraint Clone(DataTable table) {
  252. int keys = ColumnsReference.Length;
  253. DataColumn[] columns = new DataColumn[keys];
  254. for (int i = 0; i < keys; i++) {
  255. DataColumn src = ColumnsReference[i];
  256. int iDest = table.Columns.IndexOf(src.ColumnName);
  257. if (iDest < 0)
  258. return null;
  259. columns[i] = table.Columns[iDest];
  260. }
  261. UniqueConstraint clone = new UniqueConstraint(ConstraintName, columns);
  262. // ...Extended Properties
  263. foreach(Object key in this.ExtendedProperties.Keys) {
  264. clone.ExtendedProperties[key]=this.ExtendedProperties[key];
  265. }
  266. return clone;
  267. }
  268. /// <devdoc>
  269. /// <para>Gets the array of columns that this constraint affects.</para>
  270. /// </devdoc>
  271. [
  272. ResCategoryAttribute(Res.DataCategory_Data),
  273. ResDescriptionAttribute(Res.KeyConstraintColumnsDescr),
  274. ReadOnly(true)
  275. ]
  276. public virtual DataColumn[] Columns {
  277. get {
  278. return key.ToArray();
  279. }
  280. }
  281. internal DataColumn[] ColumnsReference {
  282. get {
  283. return key.ColumnsReference;
  284. }
  285. }
  286. /// <devdoc>
  287. /// <para>Gets
  288. /// a value indicating whether or not the constraint is on a primary key.</para>
  289. /// </devdoc>
  290. [
  291. ResCategoryAttribute(Res.DataCategory_Data),
  292. ResDescriptionAttribute(Res.KeyConstraintIsPrimaryKeyDescr)
  293. ]
  294. public bool IsPrimaryKey {
  295. get {
  296. if (Table == null) {
  297. return false;
  298. }
  299. return(this == Table.primaryKey);
  300. }
  301. }
  302. private void Create(String constraintName, DataColumn[] columns) {
  303. for (int i = 0; i < columns.Length; i++) {
  304. if (columns[i].Computed) {
  305. throw ExceptionBuilder.ExpressionInConstraint(columns[i]);
  306. }
  307. }
  308. this.key = new DataKey(columns, true);
  309. ConstraintName = constraintName;
  310. NonVirtualCheckState();
  311. }
  312. /// <devdoc>
  313. /// <para>Compares this constraint to a second to
  314. /// determine if both are identical.</para>
  315. /// </devdoc>
  316. public override bool Equals(object key2) {
  317. if (!(key2 is UniqueConstraint))
  318. return false;
  319. return Key.ColumnsEqual(((UniqueConstraint)key2).Key);
  320. }
  321. public override Int32 GetHashCode() {
  322. return base.GetHashCode();
  323. }
  324. internal override bool InCollection {
  325. set {
  326. base.InCollection = value;
  327. if (key.ColumnsReference.Length == 1) {
  328. key.ColumnsReference[0].InternalUnique(value);
  329. }
  330. }
  331. }
  332. internal DataKey Key {
  333. get {
  334. return key;
  335. }
  336. }
  337. /// <devdoc>
  338. /// <para>Gets the table to which this constraint belongs.</para>
  339. /// </devdoc>
  340. [
  341. ResCategoryAttribute(Res.DataCategory_Data),
  342. ResDescriptionAttribute(Res.ConstraintTableDescr),
  343. ReadOnly(true)
  344. ]
  345. public override DataTable Table {
  346. get {
  347. if (key.HasValue) {
  348. return key.Table;
  349. }
  350. return null;
  351. }
  352. }
  353. // misc
  354. }
  355. }