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;
23 using Mono.Data.SqlExpressions;
25 namespace System.Data {
26 internal delegate void DelegateColumnValueChange(DataColumn column, DataRow row, object proposedValue);
29 /// Summary description for DataColumn.
34 [DefaultMember ("Item")]
35 [DefaultProperty ("ColumnName")]
36 [DesignTimeVisible (false)]
37 [TypeConverterAttribute (typeof (ComponentConverter))]
38 public class DataColumn : MarshalByValueComponent
42 //used for constraint validation
43 //if an exception is fired during this event the change should be canceled
44 internal event DelegateColumnValueChange ValidateColumnValueChange;
46 //used for FK Constraint Cascading rules
47 internal event DelegateColumnValueChange ColumnValueChanging;
52 private bool _allowDBNull = true;
53 private bool _autoIncrement;
54 private long _autoIncrementSeed;
55 private long _autoIncrementStep = 1;
56 private long _nextAutoIncrementValue;
57 private bool dataHasBeenSet;
58 private string _caption;
59 private MappingType _columnMapping;
60 private string _columnName;
61 private Type _dataType;
62 private object _defaultValue = DBNull.Value;
63 private string expression;
64 private IExpression compiledExpression;
65 private PropertyCollection _extendedProperties = new PropertyCollection ();
66 private int maxLength = -1; //-1 represents no length limit
67 private string nameSpace = "";
68 private int _ordinal = -1; //-1 represents not part of a collection
69 private string prefix = "";
70 private bool readOnly;
71 private DataTable _table;
78 public DataColumn() : this("", typeof (string), "", MappingType.Element)
82 //TODO: Ctor init vars directly
83 public DataColumn(string columnName): this(columnName, typeof (string), "", MappingType.Element)
87 public DataColumn(string columnName, Type dataType): this(columnName, dataType, "", MappingType.Element)
91 public DataColumn( string columnName, Type dataType,
92 string expr): this(columnName, dataType, expr, MappingType.Element)
96 public DataColumn(string columnName, Type dataType,
97 string expr, MappingType type)
99 ColumnName = (columnName == null ? "" : columnName);
101 if(dataType == null) {
102 throw new ArgumentNullException("dataType can't be null.");
106 Expression = expr == null ? "" : expr;
107 ColumnMapping = type;
113 [DataCategory ("Data")]
114 [DataSysDescription ("Indicates whether null values are allowed in this column.")]
115 [DefaultValue (true)]
116 public bool AllowDBNull
122 //TODO: If we are a part of the table and this value changes
123 //we need to validate that all the existing values conform to the new setting
131 //if Value == false case
134 if (_table.Rows.Count > 0)
136 bool nullsFound = false;
137 for(int r = 0; r < _table.Rows.Count; r++) {
138 DataRow row = _table.Rows[r];
139 if(row.IsNull(this)) {
146 throw new DataException("Column '" + ColumnName + "' has null values in it.");
147 //TODO: Validate no null values exist
148 //do we also check different versions of the row??
152 _allowDBNull = value;
157 /// Gets or sets a value indicating whether the column automatically increments the value of the column for new rows added to the table.
160 /// If the type of this column is not Int16, Int32, or Int64 when this property is set,
161 /// the DataType property is coerced to Int32. An exception is generated if this is a computed column
162 /// (that is, the Expression property is set.) The incremented value is used only if the row's value for this column,
163 /// when added to the columns collection, is equal to the default value.
165 [DataCategory ("Data")]
166 [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.")]
167 [DefaultValue (false)]
168 [RefreshProperties (RefreshProperties.All)]
169 public bool AutoIncrement
172 return _autoIncrement;
177 //Can't be true if this is a computed column
178 if (Expression != string.Empty)
180 throw new ArgumentException("Can not Auto Increment a computed column.");
183 //If the DataType of this Column isn't an Int
185 TypeCode typeCode = Type.GetTypeCode(_dataType);
186 if(typeCode != TypeCode.Int16 &&
187 typeCode != TypeCode.Int32 &&
188 typeCode != TypeCode.Int64)
190 _dataType = typeof(Int32);
193 _autoIncrement = value;
197 [DataCategory ("Data")]
198 [DataSysDescription ("Indicates the starting value for an AutoIncrement column.")]
200 public long AutoIncrementSeed
203 return _autoIncrementSeed;
206 _autoIncrementSeed = value;
207 _nextAutoIncrementValue = _autoIncrementSeed;
211 [DataCategory ("Data")]
212 [DataSysDescription ("Indicates the increment used by an AutoIncrement column.")]
214 public long AutoIncrementStep
217 return _autoIncrementStep;
220 _autoIncrementStep = value;
224 internal void UpdateAutoIncrementValue (long value)
226 if(value > _nextAutoIncrementValue) {
227 _nextAutoIncrementValue = value;
228 AutoIncrementValue ();
232 internal long AutoIncrementValue ()
234 long currentValue = _nextAutoIncrementValue;
235 _nextAutoIncrementValue += AutoIncrementStep;
239 internal long GetAutoIncrementValue ()
241 return _nextAutoIncrementValue;
244 internal bool DataHasBeenSet {
246 return dataHasBeenSet;
249 dataHasBeenSet = value;
253 [DataCategory ("Data")]
254 [DataSysDescription ("Indicates the default user-interface caption for this column.")]
255 public string Caption
265 value = String.Empty;
270 [DataSysDescription ("Indicates how this column persists in XML: as an attribute, element, simple content node, or nothing.")]
271 [DefaultValue (MappingType.Element)]
272 public virtual MappingType ColumnMapping
275 return _columnMapping;
278 _columnMapping = value;
282 [DataCategory ("Data")]
283 [DataSysDescription ("Indicates the name used to look up this column in the Columns collection of a DataTable.")]
284 [RefreshProperties (RefreshProperties.All)]
286 public string ColumnName
289 return "" + _columnName;
292 //Both are checked after the column is part of the collection
293 //TODO: Check Name duplicate
294 //TODO: check Name != null
299 [DataCategory ("Data")]
300 [DataSysDescription ("Indicates the type of data stored in this column.")]
301 [DefaultValue (typeof (string))]
302 [RefreshProperties (RefreshProperties.All)]
303 [TypeConverterAttribute (typeof (ColumnTypeConverter))]
310 // check if data already exists can we change the datatype
311 if(DataHasBeenSet == true)
312 throw new ArgumentException("The column already has data stored.");
314 // we want to check that the datatype is supported?
315 TypeCode typeCode = Type.GetTypeCode(value);
317 //Check AutoIncrement status, make compatible datatype
318 if(AutoIncrement == true) {
319 if(typeCode != TypeCode.Int16 &&
320 typeCode != TypeCode.Int32 &&
321 typeCode != TypeCode.Int64)
322 AutoIncrement = false;
331 /// <remarks>When AutoIncrement is set to true, there can be no default value.</remarks>
332 /// <exception cref="System.InvalidCastException"></exception>
333 /// <exception cref="System.ArgumentException"></exception>
334 [DataCategory ("Data")]
335 [DataSysDescription ("Indicates the default column value used when adding new rows to the table.")]
336 [TypeConverterAttribute (typeof (System.Data.DefaultValueTypeConverter))]
337 public object DefaultValue
340 return _defaultValue;
344 if ((this._defaultValue == null) || (!this._defaultValue.Equals(value)))
346 //If autoIncrement == true throw
349 throw new ArgumentException("Can not set default value while" +
350 " AutoIncrement is true on this column.");
355 tmpObj = DBNull.Value;
360 if ((this.DataType != typeof (object))&& (tmpObj != DBNull.Value))
364 //Casting to the new type
365 tmpObj= Convert.ChangeType(tmpObj,this.DataType);
367 catch (InvalidCastException)
369 throw new InvalidCastException("Default Value type is not compatible with" +
373 _defaultValue = tmpObj;
378 [DataCategory ("Data")]
379 [DataSysDescription ("Indicates the value that this column computes for each row based on other columns instead of taking user input.")]
381 [RefreshProperties (RefreshProperties.All)]
382 public string Expression
389 value = String.Empty;
391 if (value != String.Empty) {
392 Parser parser = new Parser ();
393 compiledExpression = parser.Compile (value);
400 internal IExpression CompiledExpression {
401 get { return compiledExpression; }
405 [DataCategory ("Data")]
406 [DataSysDescription ("The collection that holds custom user information.")]
407 public PropertyCollection ExtendedProperties
410 return _extendedProperties;
414 [DataCategory ("Data")]
415 [DataSysDescription ("Indicates the maximum length of the value this column allows.")]
420 //Default == -1 no max length
424 //only applies to string columns
429 [DataCategory ("Data")]
430 [DataSysDescription ("Indicates the XML uri for elements stored in this column.")]
431 public string Namespace
441 //Need a good way to set the Ordinal when the column is added to a columnCollection.
443 [DataCategory ("Data")]
444 [DataSysDescription ("Indicates the index of this column in the Columns collection.")]
445 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
449 //value is -1 if not part of a collection
454 internal void SetOrdinal(int ordinal)
459 [DataCategory ("Data")]
460 [DataSysDescription ("Indicates the prefix used for this DataColumn in the xml representation.")]
472 [DataCategory ("Data")]
473 [DataSysDescription ("Indicates whether this column allows changes once a row has been added to the table.")]
474 [DefaultValue (false)]
486 [DataCategory ("Data")]
487 [DataSysDescription ("Returns the DataTable to which this column belongs.")]
488 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
489 public DataTable Table
497 [DataCategory ("Data")]
498 [DataSysDescription ("Indicates whether this column should restrict its values in the rows of the table to be unique.")]
499 [DefaultValue (false)]
500 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
507 //if Table == null then the UniqueConstraint is
508 //created on addition to the collection
510 //FIXME?: need to check if value is the same
511 //because when calling "new UniqueConstraint"
512 //the new object tries to set "column.Unique = True"
513 //which creates an infinite loop.
520 if (Expression != null && Expression != "")
521 throw new ArgumentException("Cannot change Unique property for the expression column.");
524 UniqueConstraint uc = new UniqueConstraint(this);
525 _table.Constraints.Add(uc);
532 ConstraintCollection cc = _table.Constraints;
533 //foreach (Constraint c in cc)
534 for (int i = 0; i < cc.Count; i++)
536 Constraint c = cc[i];
537 if (c is UniqueConstraint)
539 DataColumn[] cols = ((UniqueConstraint)c).Columns;
541 if (cols.Length == 1 && cols[0] == this)
543 if (!cc.CanRemove(c))
544 throw new ArgumentException("Cannot remove unique constraint '" + c.ConstraintName + "'. Remove foreign key constraint first.");
558 #endregion // Properties
564 protected internal void CheckNotAllowNull() {
568 protected void CheckUnique() {
573 /// Sets unique true whithout creating Constraint
575 internal void SetUnique()
582 internal void AssertCanAddToCollection()
584 //Check if Default Value is set and AutoInc is set
588 protected internal virtual void
589 OnPropertyChanging (PropertyChangedEventArgs pcevent) {
593 protected internal void RaisePropertyChanging(string name) {
597 /// Gets the Expression of the column, if one exists.
599 /// <returns>The Expression value, if the property is set;
600 /// otherwise, the ColumnName property.</returns>
601 public override string ToString()
603 if (expression != string.Empty)
604 return ColumnName + " + " + expression;
610 internal void SetTable(DataTable table) {
612 // this will get called by DataTable
613 // and DataColumnCollection
617 // Returns true if all the same collumns are in columnSet and compareSet
618 internal static bool AreColumnSetsTheSame(DataColumn[] columnSet, DataColumn[] compareSet)
620 if (null == columnSet && null == compareSet) return true;
621 if (null == columnSet || null == compareSet) return false;
623 if (columnSet.Length != compareSet.Length) return false;
625 foreach (DataColumn col in columnSet)
627 bool matchFound = false;
628 foreach (DataColumn compare in compareSet)
635 if (! matchFound) return false;
641 static internal int CompareValues (object val1, object val2, Type t, bool ignoreCase)
643 IComparer comparer = DBComparerFactory.GetComparer (t, ignoreCase);
644 return comparer.Compare (val1, val2);
647 #endregion // Methods