DataColumn.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. //
  2. // System.Data.DataColumn.cs
  3. //
  4. // Author:
  5. // Franklin Wise ([email protected])
  6. // Christopher Podurgiel ([email protected])
  7. // Rodrigo Moya ([email protected])
  8. // Daniel Morgan ([email protected])
  9. // Tim Coleman ([email protected])
  10. //
  11. // (C) Copyright 2002, Franklin Wise
  12. // (C) Chris Podurgiel
  13. // (C) Ximian, Inc 2002
  14. // Copyright (C) Tim Coleman, 2002
  15. // Copyright (C) Daniel Morgan, 2002, 2003
  16. //
  17. //
  18. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  19. //
  20. // Permission is hereby granted, free of charge, to any person obtaining
  21. // a copy of this software and associated documentation files (the
  22. // "Software"), to deal in the Software without restriction, including
  23. // without limitation the rights to use, copy, modify, merge, publish,
  24. // distribute, sublicense, and/or sell copies of the Software, and to
  25. // permit persons to whom the Software is furnished to do so, subject to
  26. // the following conditions:
  27. //
  28. // The above copyright notice and this permission notice shall be
  29. // included in all copies or substantial portions of the Software.
  30. //
  31. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  32. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  33. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  34. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  35. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  36. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  37. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  38. //
  39. using System;
  40. using System.ComponentModel;
  41. using System.Reflection;
  42. using System.Collections;
  43. using System.Data.Common;
  44. using Mono.Data.SqlExpressions;
  45. namespace System.Data {
  46. internal delegate void DelegateColumnValueChange(DataColumn column, DataRow row, object proposedValue);
  47. /// <summary>
  48. /// Summary description for DataColumn.
  49. /// </summary>
  50. [Editor]
  51. [ToolboxItem (false)]
  52. [DefaultProperty ("ColumnName")]
  53. [DesignTimeVisible (false)]
  54. public class DataColumn : MarshalByValueComponent
  55. {
  56. #region Events
  57. [MonoTODO]
  58. //used for constraint validation
  59. //if an exception is fired during this event the change should be canceled
  60. internal event DelegateColumnValueChange ValidateColumnValueChange;
  61. //used for FK Constraint Cascading rules
  62. internal event DelegateColumnValueChange ColumnValueChanging;
  63. #endregion //Events
  64. #region Fields
  65. private bool _allowDBNull = true;
  66. private bool _autoIncrement;
  67. private long _autoIncrementSeed;
  68. private long _autoIncrementStep = 1;
  69. private long _nextAutoIncrementValue;
  70. private string _caption;
  71. private MappingType _columnMapping;
  72. private string _columnName;
  73. private object _defaultValue = DBNull.Value;
  74. private string expression;
  75. private IExpression compiledExpression;
  76. private PropertyCollection _extendedProperties = new PropertyCollection ();
  77. private int maxLength = -1; //-1 represents no length limit
  78. private string nameSpace = String.Empty;
  79. private int _ordinal = -1; //-1 represents not part of a collection
  80. private string prefix = String.Empty;
  81. private bool readOnly;
  82. private DataTable _table;
  83. private bool unique;
  84. private AbstractDataContainer _dataContainer;
  85. #endregion // Fields
  86. #region Constructors
  87. public DataColumn() : this(String.Empty, typeof (string), String.Empty, MappingType.Element)
  88. {
  89. }
  90. //TODO: Ctor init vars directly
  91. public DataColumn(string columnName): this(columnName, typeof (string), String.Empty, MappingType.Element)
  92. {
  93. }
  94. public DataColumn(string columnName, Type dataType): this(columnName, dataType, String.Empty, MappingType.Element)
  95. {
  96. }
  97. public DataColumn( string columnName, Type dataType,
  98. string expr): this(columnName, dataType, expr, MappingType.Element)
  99. {
  100. }
  101. public DataColumn(string columnName, Type dataType,
  102. string expr, MappingType type)
  103. {
  104. ColumnName = (columnName == null ? String.Empty : columnName);
  105. if(dataType == null) {
  106. throw new ArgumentNullException("dataType can't be null.");
  107. }
  108. DataType = dataType;
  109. Expression = expr == null ? String.Empty : expr;
  110. ColumnMapping = type;
  111. }
  112. #endregion
  113. #region Properties
  114. internal object this[int index] {
  115. get {
  116. return DataContainer[index];
  117. }
  118. set {
  119. if ( !(value == null && AutoIncrement) ) {
  120. DataContainer[index] = value;
  121. }
  122. if ( AutoIncrement && !DataContainer.IsNull(index) ) {
  123. long value64 = Convert.ToInt64(value);
  124. UpdateAutoIncrementValue(value64);
  125. }
  126. }
  127. }
  128. [DataCategory ("Data")]
  129. [DataSysDescription ("Indicates whether null values are allowed in this column.")]
  130. [DefaultValue (true)]
  131. public bool AllowDBNull
  132. {
  133. get {
  134. return _allowDBNull;
  135. }
  136. set {
  137. //TODO: If we are a part of the table and this value changes
  138. //we need to validate that all the existing values conform to the new setting
  139. if (true == value)
  140. {
  141. _allowDBNull = true;
  142. return;
  143. }
  144. //if Value == false case
  145. if (null != _table)
  146. {
  147. if (_table.Rows.Count > 0)
  148. {
  149. bool nullsFound = false;
  150. for(int r = 0; r < _table.Rows.Count; r++) {
  151. DataRow row = _table.Rows[r];
  152. if(row.IsNull(this)) {
  153. nullsFound = true;
  154. break;
  155. }
  156. }
  157. if (nullsFound)
  158. throw new DataException("Column '" + ColumnName + "' has null values in it.");
  159. //TODO: Validate no null values exist
  160. //do we also check different versions of the row??
  161. }
  162. }
  163. _allowDBNull = value;
  164. }
  165. }
  166. /// <summary>
  167. /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.
  168. /// </summary>
  169. /// <remarks>
  170. /// If the type of this column is not Int16, Int32, or Int64 when this property is set,
  171. /// the DataType property is coerced to Int32. An exception is generated if this is a computed column
  172. /// (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,
  173. /// when added to the columns collection, is equal to the default value.
  174. /// </remarks>
  175. [DataCategory ("Data")]
  176. [DataSysDescription ("Indicates whether the column automatically increments itself for new rows added to the table. The type of this column must be Int16, Int32, or Int64.")]
  177. [DefaultValue (false)]
  178. [RefreshProperties (RefreshProperties.All)]
  179. public bool AutoIncrement
  180. {
  181. get {
  182. return _autoIncrement;
  183. }
  184. set {
  185. if(value == true)
  186. {
  187. //Can't be true if this is a computed column
  188. if (Expression != string.Empty)
  189. {
  190. throw new ArgumentException("Can not Auto Increment a computed column.");
  191. }
  192. if ( DefaultValue != DBNull.Value ) {
  193. throw new ArgumentException("Can not set AutoIncrement while" +
  194. " default value exists for this column.");
  195. }
  196. //If the DataType of this Column isn't an Int
  197. //Make it an int
  198. TypeCode typeCode = Type.GetTypeCode(DataType);
  199. if(typeCode != TypeCode.Int16 &&
  200. typeCode != TypeCode.Int32 &&
  201. typeCode != TypeCode.Int64)
  202. {
  203. DataType = typeof(Int32);
  204. }
  205. if (_table != null)
  206. _table.Columns.UpdateAutoIncrement(this,true);
  207. }
  208. else
  209. {
  210. if (_table != null)
  211. _table.Columns.UpdateAutoIncrement(this,false);
  212. }
  213. _autoIncrement = value;
  214. }
  215. }
  216. [DataCategory ("Data")]
  217. [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]
  218. [DefaultValue (0)]
  219. public long AutoIncrementSeed
  220. {
  221. get {
  222. return _autoIncrementSeed;
  223. }
  224. set {
  225. _autoIncrementSeed = value;
  226. _nextAutoIncrementValue = _autoIncrementSeed;
  227. }
  228. }
  229. [DataCategory ("Data")]
  230. [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]
  231. [DefaultValue (1)]
  232. public long AutoIncrementStep
  233. {
  234. get {
  235. return _autoIncrementStep;
  236. }
  237. set {
  238. _autoIncrementStep = value;
  239. }
  240. }
  241. internal void UpdateAutoIncrementValue(long value64)
  242. {
  243. if (_autoIncrementStep > 0 ) {
  244. if (value64 >= _nextAutoIncrementValue) {
  245. _nextAutoIncrementValue = value64;
  246. AutoIncrementValue ();
  247. }
  248. }
  249. else if (value64 <= _nextAutoIncrementValue) {
  250. AutoIncrementValue ();
  251. }
  252. }
  253. internal long AutoIncrementValue ()
  254. {
  255. long currentValue = _nextAutoIncrementValue;
  256. _nextAutoIncrementValue += AutoIncrementStep;
  257. return currentValue;
  258. }
  259. internal long GetAutoIncrementValue ()
  260. {
  261. return _nextAutoIncrementValue;
  262. }
  263. [DataCategory ("Data")]
  264. [DataSysDescription ("Indicates the default user-interface caption for this column.")]
  265. public string Caption
  266. {
  267. get {
  268. if(_caption == null)
  269. return ColumnName;
  270. else
  271. return _caption;
  272. }
  273. set {
  274. if (value == null)
  275. value = String.Empty;
  276. _caption = value;
  277. }
  278. }
  279. [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]
  280. [DefaultValue (MappingType.Element)]
  281. public virtual MappingType ColumnMapping
  282. {
  283. get {
  284. return _columnMapping;
  285. }
  286. set {
  287. _columnMapping = value;
  288. }
  289. }
  290. [DataCategory ("Data")]
  291. [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]
  292. [RefreshProperties (RefreshProperties.All)]
  293. [DefaultValue ("")]
  294. public string ColumnName
  295. {
  296. get {
  297. return (_columnName == null ? String.Empty : _columnName);
  298. }
  299. set {
  300. //Both are checked after the column is part of the collection
  301. //TODO: Check Name duplicate
  302. //TODO: check Name != null
  303. _columnName = value;
  304. }
  305. }
  306. [DataCategory ("Data")]
  307. [DataSysDescription ("Indicates the type of data stored in this column.")]
  308. [DefaultValue (typeof (string))]
  309. [RefreshProperties (RefreshProperties.All)]
  310. [TypeConverterAttribute (typeof (ColumnTypeConverter))]
  311. public Type DataType
  312. {
  313. get {
  314. return DataContainer.Type;
  315. }
  316. set {
  317. if ( _dataContainer != null ) {
  318. if ( value == _dataContainer.Type ) {
  319. return;
  320. }
  321. // check if data already exists can we change the datatype
  322. if ( _dataContainer.Capacity > 0 )
  323. throw new ArgumentException("The column already has data stored.");
  324. }
  325. _dataContainer = AbstractDataContainer.CreateInstance(value, this);
  326. //Check AutoIncrement status, make compatible datatype
  327. if(AutoIncrement == true) {
  328. // we want to check that the datatype is supported?
  329. TypeCode typeCode = Type.GetTypeCode(value);
  330. if(typeCode != TypeCode.Int16 &&
  331. typeCode != TypeCode.Int32 &&
  332. typeCode != TypeCode.Int64) {
  333. AutoIncrement = false;
  334. }
  335. }
  336. }
  337. }
  338. /// <summary>
  339. ///
  340. /// </summary>
  341. /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>
  342. /// <exception cref="System.InvalidCastException"></exception>
  343. /// <exception cref="System.ArgumentException"></exception>
  344. [DataCategory ("Data")]
  345. [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]
  346. [TypeConverterAttribute (typeof (System.Data.DefaultValueTypeConverter))]
  347. public object DefaultValue
  348. {
  349. get {
  350. return _defaultValue;
  351. }
  352. set {
  353. if (AutoIncrement) {
  354. throw new ArgumentException("Can not set default value while" +
  355. " AutoIncrement is true on this column.");
  356. }
  357. object tmpObj;
  358. if (!this._defaultValue.Equals(value)) {
  359. if (value == null) {
  360. tmpObj = DBNull.Value;
  361. }
  362. else {
  363. tmpObj = value;
  364. }
  365. if ((this.DataType != typeof (object))&& (tmpObj != DBNull.Value)) {
  366. try {
  367. //Casting to the new type
  368. tmpObj= Convert.ChangeType(tmpObj,this.DataType);
  369. }
  370. catch (InvalidCastException) {
  371. throw new InvalidCastException("Default Value type is not compatible with" +
  372. " column type.");
  373. }
  374. }
  375. _defaultValue = tmpObj;
  376. }
  377. // store default value in the table if already belongs to
  378. if (Table != null && Table.DefaultValuesRowIndex != -1) {
  379. DataContainer[Table.DefaultValuesRowIndex] = _defaultValue;
  380. }
  381. }
  382. }
  383. [DataCategory ("Data")]
  384. [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]
  385. [DefaultValue ("")]
  386. [RefreshProperties (RefreshProperties.All)]
  387. public string Expression
  388. {
  389. get {
  390. return expression;
  391. }
  392. set {
  393. if (value == null)
  394. value = String.Empty;
  395. if (value != String.Empty) {
  396. Parser parser = new Parser ();
  397. compiledExpression = parser.Compile (value);
  398. ReadOnly = true;
  399. }
  400. expression = value;
  401. }
  402. }
  403. internal IExpression CompiledExpression {
  404. get { return compiledExpression; }
  405. }
  406. [Browsable (false)]
  407. [DataCategory ("Data")]
  408. [DataSysDescription ("The collection that holds custom user information.")]
  409. public PropertyCollection ExtendedProperties
  410. {
  411. get {
  412. return _extendedProperties;
  413. }
  414. }
  415. [DataCategory ("Data")]
  416. [DataSysDescription ("Indicates the maximum length of the value this column allows.")]
  417. [DefaultValue (-1)]
  418. public int MaxLength
  419. {
  420. get {
  421. //Default == -1 no max length
  422. return maxLength;
  423. }
  424. set {
  425. //only applies to string columns
  426. maxLength = value;
  427. }
  428. }
  429. [DataCategory ("Data")]
  430. [DataSysDescription ("Indicates the XML uri for elements stored in this column.")]
  431. public string Namespace
  432. {
  433. get {
  434. return nameSpace;
  435. }
  436. set {
  437. if (value == null)
  438. value = String.Empty;
  439. nameSpace = value;
  440. }
  441. }
  442. //Need a good way to set the Ordinal when the column is added to a columnCollection.
  443. [Browsable (false)]
  444. [DataCategory ("Data")]
  445. [DataSysDescription ("Indicates the index of this column in the Columns collection.")]
  446. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  447. public int Ordinal
  448. {
  449. get {
  450. //value is -1 if not part of a collection
  451. return _ordinal;
  452. }
  453. }
  454. internal void SetOrdinal(int ordinal)
  455. {
  456. _ordinal = ordinal;
  457. }
  458. [DataCategory ("Data")]
  459. [DataSysDescription ("Indicates the prefix used for this DataColumn in the xml representation.")]
  460. [DefaultValue ("")]
  461. public string Prefix
  462. {
  463. get {
  464. return prefix;
  465. }
  466. set {
  467. if (value == null)
  468. value = String.Empty;
  469. prefix = value;
  470. }
  471. }
  472. [DataCategory ("Data")]
  473. [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]
  474. [DefaultValue (false)]
  475. public bool ReadOnly
  476. {
  477. get {
  478. return readOnly;
  479. }
  480. set {
  481. readOnly = value;
  482. }
  483. }
  484. [Browsable (false)]
  485. [DataCategory ("Data")]
  486. [DataSysDescription ("Returns the DataTable to which this column belongs.")]
  487. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  488. public DataTable Table
  489. {
  490. get {
  491. return _table;
  492. }
  493. }
  494. [DataCategory ("Data")]
  495. [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]
  496. [DefaultValue (false)]
  497. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  498. public bool Unique
  499. {
  500. get {
  501. return unique;
  502. }
  503. set {
  504. //NOTE: In .NET 1.1 the Unique property
  505. //is left unchanged when it is added
  506. //to a UniqueConstraint
  507. if(unique != value)
  508. {
  509. unique = value;
  510. if( value )
  511. {
  512. if (Expression != null && Expression != String.Empty)
  513. throw new ArgumentException("Cannot change Unique property for the expression column.");
  514. if( _table != null )
  515. {
  516. UniqueConstraint uc = new UniqueConstraint(this);
  517. _table.Constraints.Add(uc);
  518. }
  519. }
  520. else
  521. {
  522. if( _table != null )
  523. {
  524. ConstraintCollection cc = _table.Constraints;
  525. //foreach (Constraint c in cc)
  526. for (int i = 0; i < cc.Count; i++)
  527. {
  528. Constraint c = cc[i];
  529. if (c is UniqueConstraint)
  530. {
  531. DataColumn[] cols = ((UniqueConstraint)c).Columns;
  532. if (cols.Length == 1 && cols[0] == this)
  533. {
  534. if (!cc.CanRemove(c))
  535. throw new ArgumentException("Cannot remove unique constraint '" + c.ConstraintName + "'. Remove foreign key constraint first.");
  536. cc.Remove(c);
  537. }
  538. }
  539. }
  540. }
  541. }
  542. }
  543. }
  544. }
  545. internal AbstractDataContainer DataContainer {
  546. get {
  547. return _dataContainer;
  548. }
  549. }
  550. #endregion // Properties
  551. #region Methods
  552. /* ??
  553. [MonoTODO]
  554. protected internal void CheckNotAllowNull() {
  555. }
  556. [MonoTODO]
  557. protected void CheckUnique() {
  558. }
  559. */
  560. /// <summary>
  561. /// Sets unique true whithout creating Constraint
  562. /// </summary>
  563. internal void SetUnique()
  564. {
  565. unique = true;
  566. }
  567. [MonoTODO]
  568. internal void AssertCanAddToCollection()
  569. {
  570. //Check if Default Value is set and AutoInc is set
  571. }
  572. [MonoTODO]
  573. protected internal void CheckNotAllowNull ()
  574. {
  575. throw new NotImplementedException ();
  576. }
  577. [MonoTODO]
  578. protected void CheckUnique ()
  579. {
  580. throw new NotImplementedException ();
  581. }
  582. [MonoTODO]
  583. protected internal virtual void
  584. OnPropertyChanging (PropertyChangedEventArgs pcevent) {
  585. }
  586. [MonoTODO]
  587. protected internal void RaisePropertyChanging(string name) {
  588. }
  589. /// <summary>
  590. /// Gets the Expression of the column, if one exists.
  591. /// </summary>
  592. /// <returns>The Expression value, if the property is set;
  593. /// otherwise, the ColumnName property.</returns>
  594. public override string ToString()
  595. {
  596. if (expression != string.Empty)
  597. return ColumnName + " + " + expression;
  598. return ColumnName;
  599. }
  600. internal void SetTable(DataTable table) {
  601. if(_table!=null) { // serves as double check while adding to a table
  602. throw new ArgumentException("The column already belongs to a different table");
  603. }
  604. _table = table;
  605. // this will get called by DataTable
  606. // and DataColumnCollection
  607. if(unique) {
  608. // if the DataColumn is marked as Unique and then
  609. // added to a DataTable , then a UniqueConstraint
  610. // should be created
  611. UniqueConstraint uc = new UniqueConstraint(this);
  612. _table.Constraints.Add(uc);
  613. }
  614. // allocate space in the column data container
  615. DataContainer.Capacity = _table.RecordCache.CurrentCapacity;
  616. int defaultValuesRowIndex = _table.DefaultValuesRowIndex;
  617. if ( defaultValuesRowIndex != -1) {
  618. // store default value in the table
  619. DataContainer[defaultValuesRowIndex] = _defaultValue;
  620. // Set all the values in data container to default
  621. // it's cheaper that raise event on each row.
  622. DataContainer.FillValues(_table.DefaultValuesRowIndex);
  623. }
  624. }
  625. // Returns true if all the same collumns are in columnSet and compareSet
  626. internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)
  627. {
  628. if (null == columnSet && null == compareSet) return true;
  629. if (null == columnSet || null == compareSet) return false;
  630. if (columnSet.Length != compareSet.Length) return false;
  631. foreach (DataColumn col in columnSet)
  632. {
  633. bool matchFound = false;
  634. foreach (DataColumn compare in compareSet)
  635. {
  636. if (col == compare)
  637. {
  638. matchFound = true;
  639. }
  640. }
  641. if (! matchFound) return false;
  642. }
  643. return true;
  644. }
  645. internal int CompareValues (int index1, int index2)
  646. {
  647. return DataContainer.CompareValues(index1, index2);
  648. }
  649. #endregion // Methods
  650. }
  651. }