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;
21 using System.Collections;
22 using System.Data.Common;
24 namespace System.Data {
25 internal delegate void DelegateColumnValueChange(DataColumn column, DataRow row, object proposedValue);
28 /// Summary description for DataColumn.
33 [DefaultMember ("Item")]
34 [DefaultProperty ("ColumnName")]
35 [DesignTimeVisible (false)]
36 public class DataColumn : MarshalByValueComponent
40 //used for constraint validation
41 //if an exception is fired during this event the change should be canceled
42 internal event DelegateColumnValueChange ValidateColumnValueChange;
44 //used for FK Constraint Cascading rules
45 internal event DelegateColumnValueChange ColumnValueChanging;
50 private bool _allowDBNull = true;
51 private bool _autoIncrement;
52 private long _autoIncrementSeed;
53 private long _autoIncrementStep = 1;
54 private long _nextAutoIncrementValue;
55 private bool dataHasBeenSet;
56 private string _caption;
57 private MappingType _columnMapping;
58 private string _columnName;
59 private Type _dataType;
60 private object _defaultValue = DBNull.Value;
61 private string expression;
62 private PropertyCollection _extendedProperties = new PropertyCollection ();
63 private int maxLength = -1; //-1 represents no length limit
64 private string nameSpace = "";
65 private int _ordinal = -1; //-1 represents not part of a collection
66 private string prefix = "";
67 private bool readOnly;
68 private DataTable _table;
75 public DataColumn() : this("", typeof (string), "", MappingType.Element)
79 //TODO: Ctor init vars directly
80 public DataColumn(string columnName): this(columnName, typeof (string), "", MappingType.Element)
84 public DataColumn(string columnName, Type dataType): this(columnName, dataType, "", MappingType.Element)
88 public DataColumn( string columnName, Type dataType,
89 string expr): this(columnName, dataType, expr, MappingType.Element)
93 public DataColumn(string columnName, Type dataType,
94 string expr, MappingType type)
96 ColumnName = (columnName == null ? "" : columnName);
98 if(dataType == null) {
99 throw new ArgumentNullException("dataType can't be null.");
103 Expression = expr == null ? "" : expr;
104 ColumnMapping = type;
110 [DataCategory ("Data")]
111 [DataSysDescription ("Indicates whether null values are allowed in this column.")]
112 [DefaultValue (true)]
113 public bool AllowDBNull
119 //TODO: If we are a part of the table and this value changes
120 //we need to validate that all the existing values conform to the new setting
128 //if Value == false case
131 if (_table.Rows.Count > 0)
133 bool nullsFound = false;
134 for(int r = 0; r < _table.Rows.Count; r++) {
135 DataRow row = _table.Rows[r];
136 if(row.IsNull(this)) {
143 throw new DataException("Column '" + ColumnName + "' has null values in it.");
144 //TODO: Validate no null values exist
145 //do we also check different versions of the row??
149 _allowDBNull = value;
154 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.
157 /// If the type of this column is not Int16, Int32, or Int64 when this property is set,
158 /// the DataType property is coerced to Int32. An exception is generated if this is a computed column
159 /// (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,
160 /// when added to the columns collection, is equal to the default value.
162 [DataCategory ("Data")]
163 [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.")]
164 [DefaultValue (false)]
165 [RefreshProperties (RefreshProperties.All)]
166 public bool AutoIncrement
169 return _autoIncrement;
174 //Can't be true if this is a computed column
175 if (Expression != string.Empty)
177 throw new ArgumentException("Can not Auto Increment a computed column.");
180 //If the DataType of this Column isn't an Int
182 TypeCode typeCode = Type.GetTypeCode(_dataType);
183 if(typeCode != TypeCode.Int16 &&
184 typeCode != TypeCode.Int32 &&
185 typeCode != TypeCode.Int64)
187 _dataType = typeof(Int32);
190 _autoIncrement = value;
194 [DataCategory ("Data")]
195 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]
197 public long AutoIncrementSeed
200 return _autoIncrementSeed;
203 _autoIncrementSeed = value;
204 _nextAutoIncrementValue = _autoIncrementSeed;
208 [DataCategory ("Data")]
209 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]
211 public long AutoIncrementStep
214 return _autoIncrementStep;
217 _autoIncrementStep = value;
221 internal void UpdateAutoIncrementValue (long value)
223 if(value > _nextAutoIncrementValue) {
224 _nextAutoIncrementValue = value;
225 AutoIncrementValue ();
229 internal long AutoIncrementValue ()
231 long currentValue = _nextAutoIncrementValue;
232 _nextAutoIncrementValue += AutoIncrementStep;
236 internal long GetAutoIncrementValue ()
238 return _nextAutoIncrementValue;
241 internal bool DataHasBeenSet {
243 return dataHasBeenSet;
246 dataHasBeenSet = value;
250 [DataCategory ("Data")]
251 [DataSysDescription ("Indicates the default user-interface caption for this column.")]
252 public string Caption
264 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]
265 [DefaultValue (MappingType.Element)]
266 public virtual MappingType ColumnMapping
269 return _columnMapping;
272 _columnMapping = value;
276 [DataCategory ("Data")]
277 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]
278 [RefreshProperties (RefreshProperties.All)]
280 public string ColumnName
283 return "" + _columnName;
286 //Both are checked after the column is part of the collection
287 //TODO: Check Name duplicate
288 //TODO: check Name != null
293 [DataCategory ("Data")]
294 [DataSysDescription ("Indicates the type of data stored in this column.")]
295 [DefaultValue (typeof (string))]
296 [RefreshProperties (RefreshProperties.All)]
303 // check if data already exists can we change the datatype
304 if(DataHasBeenSet == true)
305 throw new ArgumentException("The column already has data stored.");
307 // we want to check that the datatype is supported?
308 TypeCode typeCode = Type.GetTypeCode(value);
310 //Check AutoIncrement status, make compatible datatype
311 if(AutoIncrement == true) {
312 if(typeCode != TypeCode.Int16 &&
313 typeCode != TypeCode.Int32 &&
314 typeCode != TypeCode.Int64)
315 AutoIncrement = false;
324 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>
325 /// <exception cref="System.InvalidCastException"></exception>
326 /// <exception cref="System.ArgumentException"></exception>
327 [DataCategory ("Data")]
328 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]
329 public object DefaultValue
332 return _defaultValue;
336 if ((this._defaultValue == null) || (!this._defaultValue.Equals(value)))
338 //If autoIncrement == true throw
341 throw new ArgumentException("Can not set default value while" +
342 " AutoIncrement is true on this column.");
347 tmpObj = DBNull.Value;
352 if ((this.DataType != typeof (object))&& (tmpObj != DBNull.Value))
356 //Casting to the new type
357 tmpObj= Convert.ChangeType(tmpObj,this.DataType);
359 catch (InvalidCastException)
361 throw new InvalidCastException("Default Value type is not compatible with" +
365 _defaultValue = tmpObj;
371 [DataCategory ("Data")]
372 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]
374 [RefreshProperties (RefreshProperties.All)]
375 public string Expression
381 //TODO: validation of the expression
382 expression = value; //Check?
384 //Check the validate expression of ExpressionElement with string
385 expression = System.Data.ExpressionElement.ValidateExpression(expression);
387 if (expression != string.Empty)
393 [DataCategory ("Data")]
394 [DataSysDescription ("The collection that holds custom user information.")]
395 public PropertyCollection ExtendedProperties
398 return _extendedProperties;
402 [DataCategory ("Data")]
403 [DataSysDescription ("Indicates the maximum length of the value this column allows.")]
408 //Default == -1 no max length
412 //only applies to string columns
417 [DataCategory ("Data")]
418 [DataSysDescription ("Indicates the XML uri for elements stored in this column.")]
419 public string Namespace
429 //Need a good way to set the Ordinal when the column is added to a columnCollection.
431 [DataCategory ("Data")]
432 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]
433 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
437 //value is -1 if not part of a collection
442 internal void SetOrdinal(int ordinal)
447 [DataCategory ("Data")]
448 [DataSysDescription ("Indicates the prefix used for this DataColumn in the xml representation.")]
460 [DataCategory ("Data")]
461 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]
462 [DefaultValue (false)]
474 [DataCategory ("Data")]
475 [DataSysDescription ("Returns the DataTable to which this column belongs.")]
476 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
477 public DataTable Table
485 [DataCategory ("Data")]
486 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]
487 [DefaultValue (false)]
488 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
495 //if Table == null then the UniqueConstraint is
496 //created on addition to the collection
498 //FIXME?: need to check if value is the same
499 //because when calling "new UniqueConstraint"
500 //the new object tries to set "column.Unique = True"
501 //which creates an infinite loop.
508 if (Expression != null && Expression != "")
509 throw new ArgumentException("Cannot change Unique property for the expression column.");
512 UniqueConstraint uc = new UniqueConstraint(this);
513 _table.Constraints.Add(uc);
520 ConstraintCollection cc = _table.Constraints;
521 //foreach (Constraint c in cc)
522 for (int i = 0; i < cc.Count; i++)
524 Constraint c = cc[i];
525 if (c is UniqueConstraint)
527 DataColumn[] cols = ((UniqueConstraint)c).Columns;
529 if (cols.Length == 1 && cols[0] == this)
531 if (!cc.CanRemove(c))
532 throw new ArgumentException("Cannot remove unique constraint '" + c.ConstraintName + "'. Remove foreign key constraint first.");
546 #endregion // Properties
552 protected internal void CheckNotAllowNull() {
556 protected void CheckUnique() {
561 /// Sets unique true whithout creating Constraint
563 internal void SetUnique()
570 internal void AssertCanAddToCollection()
572 //Check if Default Value is set and AutoInc is set
576 protected internal virtual void
577 OnPropertyChanging (PropertyChangedEventArgs pcevent) {
581 protected internal void RaisePropertyChanging(string name) {
585 /// Gets the Expression of the column, if one exists.
587 /// <returns>The Expression value, if the property is set;
588 /// otherwise, the ColumnName property.</returns>
590 public override string ToString()
592 if (expression != string.Empty)
593 return ColumnName + " + " + expression;
599 internal void SetTable(DataTable table) {
601 // this will get called by DataTable
602 // and DataColumnCollection
606 // Returns true if all the same collumns are in columnSet and compareSet
607 internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)
609 if (null == columnSet && null == compareSet) return true;
610 if (null == columnSet || null == compareSet) return false;
612 if (columnSet.Length != compareSet.Length) return false;
614 foreach (DataColumn col in columnSet)
616 bool matchFound = false;
617 foreach (DataColumn compare in compareSet)
624 if (! matchFound) return false;
630 static internal int CompareValues (object val1, object val2, Type t, bool ignoreCase)
632 IComparer comparer = DBComparerFactory.GetComparer (t, ignoreCase);
633 return comparer.Compare (val1, val2);
636 #endregion // Methods