2 // System.Data.DataColumn.cs
5 // Franklin Wise (gracenote@earthlink.net)
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Rodrigo Moya (rodrigo@ximian.com)
8 // Daniel Morgan (danmorg@sc.rr.com)
9 // Tim Coleman (tim@timcoleman.com)
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
19 using System.ComponentModel;
20 using System.Reflection;
22 namespace System.Data {
23 internal delegate void DelegateColumnValueChange(DataColumn column, DataRow row, object proposedValue);
26 /// Summary description for DataColumn.
31 [DefaultMember ("Item")]
32 [DefaultProperty ("ColumnName")]
33 [DesignTimeVisible (false)]
34 public class DataColumn : MarshalByValueComponent
38 //used for constraint validation
39 //if an exception is fired during this event the change should be canceled
40 internal event DelegateColumnValueChange ValidateColumnValueChange;
42 //used for FK Constraint Cascading rules
43 internal event DelegateColumnValueChange ColumnValueChanging;
48 private bool _allowDBNull = true;
49 private bool _autoIncrement = false;
50 private long _autoIncrementSeed = 0;
51 private long _autoIncrementStep = 1;
52 private long _nextAutoIncrementValue = 0;
53 private bool dataHasBeenSet = false;
54 private string _caption = null;
55 private MappingType _columnMapping = MappingType.Element;
56 private string _columnName = null;
57 private Type _dataType = Type.GetType ("System.String");
58 private object _defaultValue = DBNull.Value;
59 private string expression = "";
60 private PropertyCollection _extendedProperties = new PropertyCollection ();
61 private int maxLength = -1; //-1 represents no length limit
62 private string nameSpace = "";
63 private int _ordinal = -1; //-1 represents not part of a collection
64 private string prefix = "";
65 private bool readOnly = false;
66 private DataTable _table = null;
67 private bool unique = false;
77 //TODO: Ctor init vars directly
78 public DataColumn(string columnName): this()
80 ColumnName = columnName;
83 public DataColumn(string columnName, Type dataType): this(columnName)
85 if(dataType == null) {
86 throw new ArgumentNullException("dataType can't be null.");
93 public DataColumn( string columnName, Type dataType,
94 string expr): this(columnName, dataType)
96 if (expr != null) Expression = expr;
99 public DataColumn(string columnName, Type dataType,
100 string expr, MappingType type): this(columnName, dataType, expr)
102 ColumnMapping = type;
108 [DataCategory ("Data")]
109 [DataSysDescription ("Indicates whether null values are allowed in this column.")]
110 [DefaultValue (true)]
111 public bool AllowDBNull
117 //TODO: If we are a part of the table and this value changes
118 //we need to validate that all the existing values conform to the new setting
126 //if Value == false case
129 if (_table.Rows.Count > 0)
131 bool nullsFound = false;
132 for(int r = 0; r < _table.Rows.Count; r++) {
133 DataRow row = _table.Rows[r];
134 if(row.IsNull(this)) {
141 throw new DataException("Column '" + ColumnName + "' has null values in it.");
142 //TODO: Validate no null values exist
143 //do we also check different versions of the row??
147 _allowDBNull = value;
152 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.
155 /// If the type of this column is not Int16, Int32, or Int64 when this property is set,
156 /// the DataType property is coerced to Int32. An exception is generated if this is a computed column
157 /// (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,
158 /// when added to the columns collection, is equal to the default value.
160 [DataCategory ("Data")]
161 [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.")]
162 [DefaultValue (false)]
163 [RefreshProperties (RefreshProperties.All)]
164 public bool AutoIncrement
167 return _autoIncrement;
172 //Can't be true if this is a computed column
173 if (Expression != string.Empty)
175 throw new ArgumentException("Can not Auto Increment a computed column.");
178 //If the DataType of this Column isn't an Int
180 TypeCode typeCode = Type.GetTypeCode(_dataType);
181 if(typeCode != TypeCode.Int16 &&
182 typeCode != TypeCode.Int32 &&
183 typeCode != TypeCode.Int64)
185 _dataType = typeof(Int32);
188 _autoIncrement = value;
192 [DataCategory ("Data")]
193 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]
195 public long AutoIncrementSeed
198 return _autoIncrementSeed;
201 _autoIncrementSeed = value;
202 _nextAutoIncrementValue = _autoIncrementSeed;
206 [DataCategory ("Data")]
207 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]
209 public long AutoIncrementStep
212 return _autoIncrementStep;
215 _autoIncrementStep = value;
219 internal void UpdateAutoIncrementValue (long value)
221 if(value > _nextAutoIncrementValue) {
222 _nextAutoIncrementValue = value;
223 AutoIncrementValue ();
227 internal long AutoIncrementValue ()
229 long currentValue = _nextAutoIncrementValue;
230 _nextAutoIncrementValue += AutoIncrementStep;
234 internal bool DataHasBeenSet {
236 return dataHasBeenSet;
239 dataHasBeenSet = value;
243 [DataCategory ("Data")]
244 [DataSysDescription ("Indicates the default user-interface caption for this column.")]
245 public string Caption
257 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]
258 [DefaultValue (MappingType.Element)]
259 public virtual MappingType ColumnMapping
262 return _columnMapping;
265 _columnMapping = value;
269 [DataCategory ("Data")]
270 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]
271 [RefreshProperties (RefreshProperties.All)]
273 public string ColumnName
276 return "" + _columnName;
279 //Both are checked after the column is part of the collection
280 //TODO: Check Name duplicate
281 //TODO: check Name != null
286 [DataCategory ("Data")]
287 [DataSysDescription ("Indicates the type of data stored in this column.")]
288 [DefaultValue (typeof (string))]
289 [RefreshProperties (RefreshProperties.All)]
296 // check if data already exists can we change the datatype
297 if(DataHasBeenSet == true)
298 throw new ArgumentException("The column already has data stored.");
300 // we want to check that the datatype is supported?
301 TypeCode typeCode = Type.GetTypeCode(value);
303 //Check AutoIncrement status, make compatible datatype
304 if(AutoIncrement == true) {
305 if(typeCode != TypeCode.Int16 &&
306 typeCode != TypeCode.Int32 &&
307 typeCode != TypeCode.Int64)
308 AutoIncrement = false;
317 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>
318 /// <exception cref="System.InvalidCastException"></exception>
319 /// <exception cref="System.ArgumentException"></exception>
320 [DataCategory ("Data")]
321 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]
322 public object DefaultValue
325 return _defaultValue;
329 if ((this._defaultValue == null) || (!this._defaultValue.Equals(value)))
331 //If autoIncrement == true throw
334 throw new ArgumentException("Can not set default value while" +
335 " AutoIncrement is true on this column.");
340 tmpObj = DBNull.Value;
345 if ((this.DataType != typeof (object))&& (tmpObj != DBNull.Value))
349 //Casting to the new type
350 tmpObj= Convert.ChangeType(tmpObj,this.DataType);
352 catch (InvalidCastException)
354 throw new InvalidCastException("Default Value type is not compatible with" +
358 _defaultValue = tmpObj;
364 [DataCategory ("Data")]
365 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]
367 [RefreshProperties (RefreshProperties.All)]
368 public string Expression
374 //TODO: validation of the expression
375 expression = value; //Check?
377 //Check the validate expression of ExpressionElement with string
378 expression = System.Data.ExpressionElement.ValidateExpression(expression);
380 if (expression != string.Empty)
386 [DataCategory ("Data")]
387 [DataSysDescription ("The collection that holds custom user information.")]
388 public PropertyCollection ExtendedProperties
391 return _extendedProperties;
395 [DataCategory ("Data")]
396 [DataSysDescription ("Indicates the maximum length of the value this column allows.")]
401 //Default == -1 no max length
405 //only applies to string columns
410 [DataCategory ("Data")]
411 [DataSysDescription ("Indicates the XML uri for elements stored in this column.")]
412 public string Namespace
422 //Need a good way to set the Ordinal when the column is added to a columnCollection.
424 [DataCategory ("Data")]
425 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]
426 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
430 //value is -1 if not part of a collection
435 internal void SetOrdinal(int ordinal)
440 [DataCategory ("Data")]
441 [DataSysDescription ("Indicates the prefix used for this DataColumn in the xml representation.")]
453 [DataCategory ("Data")]
454 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]
455 [DefaultValue (false)]
467 [DataCategory ("Data")]
468 [DataSysDescription ("Returns the DataTable to which this column belongs.")]
469 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
470 public DataTable Table
478 [DataCategory ("Data")]
479 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]
480 [DefaultValue (false)]
481 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
488 //if Table == null then the UniqueConstraint is
489 //created on addition to the collection
491 //FIXME?: need to check if value is the same
492 //because when calling "new UniqueConstraint"
493 //the new object tries to set "column.Unique = True"
494 //which creates an infinite loop.
501 if (Expression != null && Expression != "")
502 throw new ArgumentException("Cannot change Unique property for the expression column.");
505 UniqueConstraint uc = new UniqueConstraint(this);
506 _table.Constraints.Add(uc);
513 ConstraintCollection cc = _table.Constraints;
514 //foreach (Constraint c in cc)
515 for (int i = 0; i < cc.Count; i++)
517 Constraint c = cc[i];
518 if (c is UniqueConstraint)
520 DataColumn[] cols = ((UniqueConstraint)c).Columns;
522 if (cols.Length == 1 && cols[0] == this)
524 if (!cc.CanRemove(c))
525 throw new ArgumentException("Cannot remove unique constraint '" + c.ConstraintName + "'. Remove foreign key constraint first.");
539 #endregion // Properties
545 protected internal void CheckNotAllowNull() {
549 protected void CheckUnique() {
554 /// Sets unique true whithout creating Constraint
556 internal void SetUnique()
563 internal void AssertCanAddToCollection()
565 //Check if Default Value is set and AutoInc is set
569 protected internal virtual void
570 OnPropertyChanging (PropertyChangedEventArgs pcevent) {
574 protected internal void RaisePropertyChanging(string name) {
578 /// Gets the Expression of the column, if one exists.
580 /// <returns>The Expression value, if the property is set;
581 /// otherwise, the ColumnName property.</returns>
583 public override string ToString()
585 if (expression != string.Empty)
586 return ColumnName + " + " + expression;
592 internal void SetTable(DataTable table) {
594 // this will get called by DataTable
595 // and DataColumnCollection
599 // Returns true if all the same collumns are in columnSet and compareSet
600 internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)
602 if (null == columnSet && null == compareSet) return true;
603 if (null == columnSet || null == compareSet) return false;
605 if (columnSet.Length != compareSet.Length) return false;
607 foreach (DataColumn col in columnSet)
609 bool matchFound = false;
610 foreach (DataColumn compare in compareSet)
617 if (! matchFound) return false;
623 #endregion // Methods