2 // System.Data.DataTable.cs
\r
5 // Franklin Wise <gracenote@earthlink.net>
\r
6 // Christopher Podurgiel (cpodurgiel@msn.com)
\r
7 // Daniel Morgan <danmorg@sc.rr.com>
\r
8 // Rodrigo Moya <rodrigo@ximian.com>
\r
9 // Tim Coleman (tim@timcoleman.com)
\r
10 // Ville Palo <vi64pa@koti.soon.fi>
\r
12 // (C) Chris Podurgiel
\r
13 // (C) Ximian, Inc 2002
\r
14 // Copyright (C) Tim Coleman, 2002
\r
15 // Copyright (C) Daniel Morgan, 2002-2003
\r
19 using System.Collections;
\r
20 using System.ComponentModel;
\r
21 using System.Globalization;
\r
22 using System.Runtime.Serialization;
\r
24 namespace System.Data {
\r
26 [ToolboxItem (false)]
\r
27 [DefaultEvent ("RowChanging")]
\r
28 [DefaultProperty ("TableName")]
\r
29 [DesignTimeVisible (false)]
\r
31 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
\r
33 internal DataSet dataSet;
\r
35 private bool _caseSensitive;
\r
36 private DataColumnCollection _columnCollection;
\r
37 private ConstraintCollection _constraintCollection;
\r
38 private DataView _defaultView;
\r
40 private string _displayExpression;
\r
41 private PropertyCollection _extendedProperties;
\r
42 private bool _hasErrors;
\r
43 private CultureInfo _locale;
\r
44 private int _minimumCapacity;
\r
45 private string _nameSpace;
\r
46 private DataRelationCollection _childRelations;
\r
47 private DataRelationCollection _parentRelations;
\r
48 private string _prefix;
\r
49 private DataColumn[] _primaryKey;
\r
50 private DataRowCollection _rows;
\r
51 private ISite _site;
\r
52 private string _tableName;
\r
53 private bool _containsListCollection;
\r
54 private string _encodedTableName;
\r
55 internal bool _duringDataLoad;
\r
56 private bool dataSetPrevEnforceConstraints;
\r
60 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
\r
61 // CaseSensitive property. So when you lost you virginity it's gone for ever
\r
62 private bool _virginCaseSensitive = true;
\r
65 /// Initializes a new instance of the DataTable class with no arguments.
\r
68 public DataTable ()
\r
71 _columnCollection = new DataColumnCollection(this);
\r
72 _constraintCollection = new ConstraintCollection(this);
\r
73 _extendedProperties = new PropertyCollection();
\r
76 _caseSensitive = false; //default value
\r
77 _displayExpression = null;
\r
80 _rows = new DataRowCollection (this);
\r
82 //LAMESPEC: spec says 25 impl does 50
\r
83 _minimumCapacity = 50;
\r
85 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
86 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
89 _defaultView = new DataView(this);
\r
93 /// Intitalizes a new instance of the DataTable class with the specified table name.
\r
95 public DataTable (string tableName) : this ()
\r
97 _tableName = tableName;
\r
101 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
\r
104 protected DataTable (SerializationInfo info, StreamingContext context)
\r
108 // TODO: Add constructor logic here
\r
113 /// Indicates whether string comparisons within the table are case-sensitive.
\r
115 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
\r
116 public bool CaseSensitive {
\r
117 get { return _caseSensitive; }
\r
119 _virginCaseSensitive = false;
\r
120 _caseSensitive = value;
\r
124 internal bool VirginCaseSensitive {
\r
125 get { return _virginCaseSensitive; }
\r
126 set { _virginCaseSensitive = value; }
\r
129 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
\r
131 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
132 OnColumnChanged(e);
\r
135 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
\r
137 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
138 OnColumnChanging (e);
\r
141 internal void DeletedDataRow (DataRow dr, DataRowAction action)
\r
143 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
147 internal void DeletingDataRow (DataRow dr, DataRowAction action)
\r
149 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
153 internal void ChangedDataRow (DataRow dr, DataRowAction action)
\r
155 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
159 internal void ChangingDataRow (DataRow dr, DataRowAction action)
\r
161 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
166 /// Gets the collection of child relations for this DataTable.
\r
168 [Browsable (false)]
\r
169 [DataSysDescription ("Returns the child relations for this table.")]
\r
170 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
171 public DataRelationCollection ChildRelations {
\r
173 return _childRelations;
\r
178 /// Gets the collection of columns that belong to this table.
\r
180 [DataCategory ("Data")]
\r
181 [DataSysDescription ("The collection that holds the columns for this table.")]
\r
182 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
183 public DataColumnCollection Columns {
\r
184 get { return _columnCollection; }
\r
188 /// Gets the collection of constraints maintained by this table.
\r
190 [DataCategory ("Data")]
\r
191 [DataSysDescription ("The collection that holds the constraints for this table.")]
\r
192 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
193 public ConstraintCollection Constraints {
\r
194 get { return _constraintCollection; }
\r
198 /// Gets the DataSet that this table belongs to.
\r
200 [Browsable (false)]
\r
201 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
\r
202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
203 public DataSet DataSet {
\r
204 get { return dataSet; }
\r
210 /// Gets a customized view of the table which may
\r
211 /// include a filtered view, or a cursor position.
\r
214 [Browsable (false)]
\r
215 [DataSysDescription ("This is the default DataView for the table.")]
\r
216 public DataView DefaultView {
\r
217 get { return _defaultView; }
\r
222 /// Gets or sets the expression that will return
\r
223 /// a value used to represent this table in the user interface.
\r
225 [DataCategory ("Data")]
\r
226 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
\r
227 [DefaultValue ("")]
\r
228 public string DisplayExpression {
\r
229 get { return "" + _displayExpression; }
\r
230 set { _displayExpression = value; }
\r
234 /// Gets the collection of customized user information.
\r
236 [Browsable (false)]
\r
237 [DataCategory ("Data")]
\r
238 [DataSysDescription ("The collection that holds custom user information.")]
\r
239 public PropertyCollection ExtendedProperties {
\r
240 get { return _extendedProperties; }
\r
244 /// Gets a value indicating whether there are errors in
\r
245 /// any of the_rows in any of the tables of the DataSet to
\r
246 /// which the table belongs.
\r
248 [Browsable (false)]
\r
249 [DataSysDescription ("Returns whether the table has errors.")]
\r
250 public bool HasErrors {
\r
252 // we can not use the _hasError flag because we do not know when to turn it off!
\r
253 for (int i = 0; i < _rows.Count; i++)
\r
255 if (_rows[i].HasErrors)
\r
263 /// Gets or sets the locale information used to
\r
264 /// compare strings within the table.
\r
266 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
\r
267 public CultureInfo Locale {
\r
269 // if the locale is null, we check for the DataSet locale
\r
270 // and if the DataSet is null we return the current culture.
\r
271 // this way if DataSet locale is changed, only if there is no locale for
\r
272 // the DataTable it influece the Locale get;
\r
273 if (_locale != null)
\r
275 if (DataSet != null)
\r
276 return DataSet.Locale;
\r
277 return CultureInfo.CurrentCulture;
\r
280 if (_locale == null || !_locale.Equals(value))
\r
286 /// Gets or sets the initial starting size for this table.
\r
288 [DataCategory ("Data")]
\r
289 [DataSysDescription ("Indicates an initial starting size for this table.")]
\r
290 [DefaultValue (50)]
\r
291 public int MinimumCapacity {
\r
292 get { return _minimumCapacity; }
\r
293 set { _minimumCapacity = value; }
\r
297 /// Gets or sets the namespace for the XML represenation
\r
298 /// of the data stored in the DataTable.
\r
300 [DataCategory ("Data")]
\r
301 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
\r
302 public string Namespace {
\r
303 get { return "" + _nameSpace; }
\r
304 set { _nameSpace = value; }
\r
308 /// Gets the collection of parent relations for
\r
309 /// this DataTable.
\r
311 [Browsable (false)]
\r
312 [DataSysDescription ("Returns the parent relations for this table.")]
\r
313 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
314 public DataRelationCollection ParentRelations {
\r
316 return _parentRelations;
\r
321 /// Gets or sets the namespace for the XML represenation
\r
322 /// of the data stored in the DataTable.
\r
324 [DataCategory ("Data")]
\r
325 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
\r
326 [DefaultValue ("")]
\r
327 public string Prefix {
\r
328 get { return "" + _prefix; }
\r
329 set { _prefix = value; }
\r
333 /// Gets or sets an array of columns that function as
\r
334 /// primary keys for the data table.
\r
336 [DataCategory ("Data")]
\r
337 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
\r
338 public DataColumn[] PrimaryKey {
\r
340 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
\r
341 if (null == uc) return new DataColumn[] {};
\r
346 //YUK: msft removes a previous unique constraint if it is flagged as a pk
\r
347 //when a new pk is set
\r
349 //clear Primary Key if value == null
\r
350 if (null == value) {
\r
352 RemoveUniqueConstraints ();
\r
356 //Does constraint exist for these columns
\r
357 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
\r
358 this.Constraints, (DataColumn[]) value);
\r
360 //if constraint doesn't exist for columns
\r
361 //create new unique primary key constraint
\r
364 RemoveUniqueConstraints ();
\r
366 foreach (DataColumn Col in (DataColumn[]) value) {
\r
368 if (Col.Table == null)
\r
371 if (Columns.IndexOf (Col) < 0)
\r
372 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
\r
376 uc = new UniqueConstraint( (DataColumn[]) value, true);
\r
378 Constraints.Add (uc);
\r
380 else { //set existing constraint as the new primary key
\r
381 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
\r
388 /// Gets the collection of_rows that belong to this table.
\r
390 [Browsable (false)]
\r
391 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
\r
392 public DataRowCollection Rows {
\r
393 get { return _rows; }
\r
397 /// Gets or sets an System.ComponentModel.ISite
\r
398 /// for the DataTable.
\r
400 [Browsable (false)]
\r
401 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
402 public override ISite Site {
\r
403 get { return _site; }
\r
404 set { _site = value; }
\r
408 /// Gets or sets the name of the the DataTable.
\r
410 [DataCategory ("Data")]
\r
411 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
\r
412 [DefaultValue ("")]
\r
413 [RefreshProperties (RefreshProperties.All)]
\r
414 public string TableName {
\r
415 get { return "" + _tableName; }
\r
416 set { _tableName = value; }
\r
419 bool IListSource.ContainsListCollection {
\r
421 // the collection is a DataView
\r
427 /// Commits all the changes made to this table since the
\r
428 /// last time AcceptChanges was called.
\r
430 public void AcceptChanges ()
\r
432 //FIXME: Do we need to validate anything here or
\r
433 //try to catch any errors to deal with them?
\r
435 // we do not use foreach because if one of the rows is in Delete state
\r
436 // it will be romeved from Rows and we get an exception.
\r
438 for (int i = 0; i < Rows.Count; )
\r
441 myRow.AcceptChanges();
\r
443 // if the row state is Detached it meens that it was removed from row list (Rows)
\r
444 // so we should not increase 'i'.
\r
445 if (myRow.RowState != DataRowState.Detached)
\r
451 /// Begins the initialization of a DataTable that is used
\r
452 /// on a form or used by another component. The initialization
\r
453 /// occurs at runtime.
\r
455 public void BeginInit ()
\r
460 /// Turns off notifications, index maintenance, and
\r
461 /// constraints while loading data.
\r
464 public void BeginLoadData ()
\r
466 if (!this._duringDataLoad)
\r
468 //duringDataLoad is important to EndLoadData and
\r
469 //for not throwing unexpected exceptions.
\r
470 this._duringDataLoad = true;
\r
472 if (this.dataSet != null)
\r
474 //Saving old Enforce constraints state for later
\r
475 //use in the EndLoadData.
\r
476 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
\r
477 this.dataSet.EnforceConstraints = false;
\r
484 /// Clears the DataTable of all data.
\r
486 public void Clear () {
\r
487 // TODO: thow an exception if any rows that
\r
488 // have enforced child relations
\r
489 // that would result in child rows being orphaned
\r
490 // now we check if any ForeignKeyConstraint is referncing 'table'.
\r
491 if (DataSet != null)
\r
493 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
\r
495 // loop on all tables in dataset
\r
496 while (tableEnumerator.MoveNext())
\r
498 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
\r
499 // loop on all constrains in the current table
\r
500 while (constraintEnumerator.MoveNext())
\r
502 Object o = constraintEnumerator.Current;
\r
503 // we only check ForeignKeyConstraint
\r
504 if (o is ForeignKeyConstraint)
\r
506 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
\r
507 if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
\r
508 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
\r
514 // TODO: throw a NotSupportedException if the DataTable is part
\r
515 // of a DataSet that is binded to an XmlDataDocument
\r
521 /// Clones the structure of the DataTable, including
\r
522 /// all DataTable schemas and constraints.
\r
525 public virtual DataTable Clone ()
\r
527 DataTable Copy = new DataTable ();
\r
529 CopyProperties (Copy);
\r
534 /// Computes the given expression on the current_rows that
\r
535 /// pass the filter criteria.
\r
538 public object Compute (string expression, string filter)
\r
540 // TODO: implement this function based
\r
541 // on Select (expression)
\r
543 // expression is an aggregate function
\r
544 // filter is an expression used to limit rows
\r
549 ExpressionElement Expression = new ExpressionMainElement (filter);
\r
551 ArrayList List = new ArrayList ();
\r
552 foreach (DataRow Row in Rows) {
\r
554 if (Expression.Test (Row))
\r
558 DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
\r
560 // TODO: with the filtered rows, execute the aggregate function
\r
561 // mentioned in expression
\r
567 /// Copies both the structure and data for this DataTable.
\r
570 public DataTable Copy ()
\r
572 DataTable Copy = new DataTable ();
\r
573 CopyProperties (Copy);
\r
575 // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
\r
576 // because if the row state is deleted we get an exception.
\r
577 Copy._duringDataLoad = true;
\r
578 foreach (DataRow Row in Rows) {
\r
579 DataRow NewRow = Copy.NewRow ();
\r
580 Copy.Rows.Add (NewRow);
\r
581 Row.CopyValuesToRow(NewRow);
\r
583 Copy._duringDataLoad = false;
\r
588 private void CopyProperties (DataTable Copy)
\r
591 Copy.CaseSensitive = CaseSensitive;
\r
592 Copy.VirginCaseSensitive = VirginCaseSensitive;
\r
594 // Copy.ChildRelations
\r
595 // Copy.Constraints
\r
597 // Copy.DefaultView
\r
599 Copy.DisplayExpression = DisplayExpression;
\r
600 // Copy.ExtendedProperties
\r
601 Copy.Locale = Locale;
\r
602 Copy.MinimumCapacity = MinimumCapacity;
\r
603 Copy.Namespace = Namespace;
\r
604 // Copy.ParentRelations
\r
605 Copy.Prefix = Prefix;
\r
607 Copy.TableName = TableName;
\r
612 foreach (DataColumn Column in Columns) {
\r
614 Copy.Columns.Add (CopyColumn (Column));
\r
617 CopyConstraints(Copy);
\r
618 // add primary key to the copy
\r
619 if (PrimaryKey.Length > 0)
\r
621 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
\r
622 for (int i = 0; i < pColumns.Length; i++)
\r
623 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
\r
625 Copy.PrimaryKey = pColumns;
\r
629 private void CopyConstraints(DataTable copy)
\r
631 UniqueConstraint origUc;
\r
632 UniqueConstraint copyUc;
\r
633 for (int i = 0; i < this.Constraints.Count; i++)
\r
635 if (this.Constraints[i] is UniqueConstraint)
\r
637 origUc = (UniqueConstraint)this.Constraints[i];
\r
638 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
\r
639 for (int j = 0; j < columns.Length; j++)
\r
640 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
\r
642 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
\r
643 copy.Constraints.Add(copyUc);
\r
648 /// Ends the initialization of a DataTable that is used
\r
649 /// on a form or used by another component. The
\r
650 /// initialization occurs at runtime.
\r
653 public void EndInit ()
\r
659 /// Turns on notifications, index maintenance, and
\r
660 /// constraints after loading data.
\r
663 public void EndLoadData()
\r
666 if (this._duringDataLoad)
\r
668 //Returning from loading mode, raising exceptions as usual
\r
669 this._duringDataLoad = false;
\r
671 if (this.dataSet !=null)
\r
673 //Getting back to previous EnforceConstraint state
\r
674 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
\r
676 for (i=0 ; i<this.Rows.Count ; i++)
\r
678 if (this.Rows[i]._nullConstraintViolation )
\r
680 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
\r
690 /// Gets a copy of the DataTable that contains all
\r
691 /// changes made to it since it was loaded or
\r
692 /// AcceptChanges was last called.
\r
695 public DataTable GetChanges()
\r
697 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
\r
701 /// Gets a copy of the DataTable containing all
\r
702 /// changes made to it since it was last loaded, or
\r
703 /// since AcceptChanges was called, filtered by DataRowState.
\r
706 public DataTable GetChanges(DataRowState rowStates)
\r
708 DataTable copyTable = null;
\r
710 IEnumerator rowEnumerator = Rows.GetEnumerator();
\r
711 while (rowEnumerator.MoveNext()) {
\r
712 DataRow row = (DataRow)rowEnumerator.Current;
\r
713 if (row.IsRowChanged(rowStates)) {
\r
714 if (copyTable == null)
\r
715 copyTable = Clone();
\r
716 DataRow newRow = copyTable.NewRow();
\r
717 copyTable.Rows.Add(newRow);
\r
718 row.CopyValuesToRow(newRow);
\r
726 /// Gets an array of DataRow objects that contain errors.
\r
729 public DataRow[] GetErrors ()
\r
731 ArrayList errors = new ArrayList();
\r
732 for (int i = 0; i < _rows.Count; i++)
\r
734 if (_rows[i].HasErrors)
\r
735 errors.Add(_rows[i]);
\r
738 return (DataRow[]) errors.ToArray(typeof(DataRow));
\r
742 /// This member is only meant to support Mono's infrastructure
\r
744 protected virtual DataTable CreateInstance ()
\r
746 return Activator.CreateInstance (this.GetType (), true) as DataTable;
\r
750 /// This member is only meant to support Mono's infrastructure
\r
752 protected virtual Type GetRowType ()
\r
754 return typeof (DataRow);
\r
758 /// This member is only meant to support Mono's infrastructure
\r
760 /// Used for Data Binding between System.Web.UI. controls
\r
761 /// like a DataGrid
\r
763 /// System.Windows.Forms controls like a DataGrid
\r
765 IList IListSource.GetList ()
\r
767 IList list = (IList) _defaultView;
\r
772 /// Copies a DataRow into a DataTable, preserving any
\r
773 /// property settings, as well as original and current values.
\r
776 public void ImportRow (DataRow row)
\r
778 DataRow newRow = NewRow();
\r
780 row.CopyValuesToRow(newRow);
\r
785 /// This member is only meant to support Mono's infrastructure
\r
788 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
\r
793 /// Finds and updates a specific row. If no matching row
\r
794 /// is found, a new row is created using the given values.
\r
797 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
\r
799 DataRow row = null;
\r
800 if (PrimaryKey.Length == 0) {
\r
801 row = Rows.Add (values);
\r
802 if (fAcceptChanges)
\r
803 row.AcceptChanges ();
\r
806 bool hasPrimaryValues = true;
\r
807 // initiate an array that has the values of the primary keys.
\r
808 object[] keyValues = new object[PrimaryKey.Length];
\r
809 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
\r
811 if(PrimaryKey[i].Ordinal < values.Length)
\r
812 keyValues[i] = values[PrimaryKey[i].Ordinal];
\r
814 hasPrimaryValues = false;
\r
817 if (hasPrimaryValues){
\r
818 // find the row in the table.
\r
819 row = Rows.Find(keyValues);
\r
823 row = Rows.Add (values);
\r
825 row.ItemArray = values;
\r
827 if (fAcceptChanges)
\r
828 row.AcceptChanges ();
\r
835 /// Creates a new DataRow with the same schema as the table.
\r
837 public DataRow NewRow ()
\r
839 return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
\r
843 /// This member supports the .NET Framework infrastructure
\r
844 /// and is not intended to be used directly from your code.
\r
846 protected internal DataRow[] NewRowArray (int size)
\r
848 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
\r
852 /// Creates a new row from an existing row.
\r
854 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
856 return new DataRow (builder);
\r
861 /// Rolls back all changes that have been made to the
\r
862 /// table since it was loaded, or the last time AcceptChanges
\r
866 public void RejectChanges ()
\r
868 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
869 DataRow row = _rows [i];
\r
870 if (row.RowState != DataRowState.Unchanged)
\r
871 _rows [i].RejectChanges ();
\r
876 /// Resets the DataTable to its original state.
\r
879 public virtual void Reset ()
\r
882 while (ParentRelations.Count > 0)
\r
884 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
\r
885 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
\r
888 while (ChildRelations.Count > 0)
\r
890 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
\r
891 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
\r
893 Constraints.Clear();
\r
898 /// Gets an array of all DataRow objects.
\r
900 public DataRow[] Select ()
\r
902 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
906 /// Gets an array of all DataRow objects that match
\r
907 /// the filter criteria in order of primary key (or
\r
908 /// lacking one, order of addition.)
\r
910 public DataRow[] Select (string filterExpression)
\r
912 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
916 /// Gets an array of all DataRow objects that
\r
917 /// match the filter criteria, in the the
\r
918 /// specified sort order.
\r
920 public DataRow[] Select (string filterExpression, string sort)
\r
922 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
\r
926 /// Gets an array of all DataRow objects that match
\r
927 /// the filter in the order of the sort, that match
\r
928 /// the specified state.
\r
931 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
\r
933 DataRow[] dataRows = null;
\r
934 if (filterExpression == null)
\r
935 filterExpression = String.Empty;
\r
937 ExpressionElement Expression = null;
\r
938 if (filterExpression != null && filterExpression.Length != 0)
\r
939 Expression = new ExpressionMainElement(filterExpression);
\r
941 ArrayList List = new ArrayList();
\r
942 int recordStateFilter = GetRowStateFilter(recordStates);
\r
943 foreach (DataRow Row in Rows)
\r
945 if (((int)Row.RowState & recordStateFilter) != 0)
\r
947 if (Expression == null || Expression.Test(Row))
\r
952 dataRows = (DataRow[])List.ToArray(typeof(DataRow));
\r
955 if (sort != null && !sort.Equals(String.Empty))
\r
957 SortableColumn[] sortableColumns = null;
\r
959 sortableColumns = ParseTheSortString (sort);
\r
960 if (sortableColumns == null)
\r
961 throw new Exception ("sort expression result is null");
\r
962 if (sortableColumns.Length == 0)
\r
963 throw new Exception("sort expression result is 0");
\r
965 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
\r
966 dataRows = rowSorter.SortRows ();
\r
968 sortableColumns = null;
\r
976 private static int GetRowStateFilter(DataViewRowState recordStates)
\r
980 if ((recordStates & DataViewRowState.Added) != 0)
\r
981 flag |= (int)DataRowState.Added;
\r
982 if ((recordStates & DataViewRowState.Deleted) != 0)
\r
983 flag |= (int)DataRowState.Deleted;
\r
984 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
\r
985 flag |= (int)DataRowState.Modified;
\r
986 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
\r
987 flag |= (int)DataRowState.Modified;
\r
988 if ((recordStates & DataViewRowState.Unchanged) != 0)
\r
989 flag |= (int)DataRowState.Unchanged;
\r
995 /// Gets the TableName and DisplayExpression, if
\r
996 /// there is one as a concatenated string.
\r
998 public override string ToString()
\r
1000 //LAMESPEC: spec says concat the two. impl puts a
\r
1001 //plus sign infront of DisplayExpression
\r
1002 string retVal = TableName;
\r
1003 if(DisplayExpression != null && DisplayExpression != "")
\r
1004 retVal += " + " + DisplayExpression;
\r
1009 #region Events /////////////////
\r
1012 /// Raises the ColumnChanged event.
\r
1014 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
\r
1015 if (null != ColumnChanged) {
\r
1016 ColumnChanged (this, e);
\r
1021 /// Raises the ColumnChanging event.
\r
1023 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
\r
1024 if (null != ColumnChanging) {
\r
1025 ColumnChanging (this, e);
\r
1030 /// Raises the PropertyChanging event.
\r
1033 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
\r
1034 // if (null != PropertyChanging)
\r
1036 // PropertyChanging (this, e);
\r
1041 /// Notifies the DataTable that a DataColumn is being removed.
\r
1044 protected internal virtual void OnRemoveColumn (DataColumn column) {
\r
1045 // if (null != RemoveColumn)
\r
1047 // RemoveColumn(this, e);
\r
1052 /// Raises the RowChanged event.
\r
1054 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
\r
1055 if (null != RowChanged) {
\r
1056 RowChanged(this, e);
\r
1062 /// Raises the RowChanging event.
\r
1064 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
\r
1065 if (null != RowChanging) {
\r
1066 RowChanging(this, e);
\r
1071 /// Raises the RowDeleted event.
\r
1073 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
\r
1074 if (null != RowDeleted) {
\r
1075 RowDeleted(this, e);
\r
1080 /// Raises the RowDeleting event.
\r
1082 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
\r
1083 if (null != RowDeleting) {
\r
1084 RowDeleting(this, e);
\r
1089 private DataColumn CopyColumn (DataColumn Column) {
\r
1090 DataColumn Copy = new DataColumn ();
\r
1092 // Copy all the properties of column
\r
1093 Copy.AllowDBNull = Column.AllowDBNull;
\r
1094 Copy.AutoIncrement = Column.AutoIncrement;
\r
1095 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
\r
1096 Copy.AutoIncrementStep = Column.AutoIncrementStep;
\r
1097 Copy.Caption = Column.Caption;
\r
1098 Copy.ColumnMapping = Column.ColumnMapping;
\r
1099 Copy.ColumnName = Column.ColumnName;
\r
1101 Copy.DataType = Column.DataType;
\r
1102 Copy.DefaultValue = Column.DefaultValue;
\r
1103 Copy.Expression = Column.Expression;
\r
1104 //Copy.ExtendedProperties
\r
1105 Copy.MaxLength = Column.MaxLength;
\r
1106 Copy.Namespace = Column.Namespace;
\r
1107 Copy.Prefix = Column.Prefix;
\r
1108 Copy.ReadOnly = Column.ReadOnly;
\r
1110 //we do not copy the unique value - it will be copyied when copying the constraints.
\r
1111 //Copy.Unique = Column.Unique;
\r
1117 /// Occurs when after a value has been changed for
\r
1118 /// the specified DataColumn in a DataRow.
\r
1120 [DataCategory ("Data")]
\r
1121 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1122 public event DataColumnChangeEventHandler ColumnChanged;
\r
1125 /// Occurs when a value is being changed for the specified
\r
1126 /// DataColumn in a DataRow.
\r
1128 [DataCategory ("Data")]
\r
1129 [DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
\r
1130 public event DataColumnChangeEventHandler ColumnChanging;
\r
1133 /// Occurs after a DataRow has been changed successfully.
\r
1135 [DataCategory ("Data")]
\r
1136 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1137 public event DataRowChangeEventHandler RowChanged;
\r
1140 /// Occurs when a DataRow is changing.
\r
1142 [DataCategory ("Data")]
\r
1143 [DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an exception to cancel the edit.")]
\r
1144 public event DataRowChangeEventHandler RowChanging;
\r
1147 /// Occurs after a row in the table has been deleted.
\r
1149 [DataCategory ("Data")]
\r
1150 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1151 public event DataRowChangeEventHandler RowDeleted;
\r
1154 /// Occurs before a row in the table is about to be deleted.
\r
1156 [DataCategory ("Data")]
\r
1157 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1158 public event DataRowChangeEventHandler RowDeleting;
\r
1160 #endregion // Events
\r
1163 /// Removes all UniqueConstraints
\r
1165 private void RemoveUniqueConstraints ()
\r
1167 foreach (Constraint Cons in Constraints) {
\r
1169 if (Cons is UniqueConstraint) {
\r
1170 Constraints.Remove (Cons);
\r
1175 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
\r
1178 // to parse the sort string for DataTable:Select(expression,sort)
\r
1179 // into sortable columns (think ORDER BY,
\r
1180 // such as, "customer ASC, price DESC" )
\r
1181 private SortableColumn[] ParseTheSortString (string sort)
\r
1183 SortableColumn[] sortColumns = null;
\r
1184 ArrayList columns = null;
\r
1186 if (sort != null && !sort.Equals ("")) {
\r
1187 columns = new ArrayList ();
\r
1188 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1190 for (int c = 0; c < columnExpression.Length; c++) {
\r
1191 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
1193 string columnName = columnSortInfo[0].Trim ();
\r
1194 string sortOrder = "ASC";
\r
1195 if (columnSortInfo.Length > 1)
\r
1196 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
\r
1198 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
1199 switch (sortOrder) {
\r
1201 sortDirection = ListSortDirection.Ascending;
\r
1204 sortDirection = ListSortDirection.Descending;
\r
1207 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
1211 ord = Int32.Parse (columnName);
\r
1213 catch (FormatException) {
\r
1216 DataColumn dc = null;
\r
1218 dc = _columnCollection[columnName];
\r
1220 dc = _columnCollection[ord];
\r
1221 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
\r
1222 columns.Add (sortCol);
\r
1224 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
\r
1226 return sortColumns;
\r
1229 private class SortableColumn
\r
1231 private DataColumn col;
\r
1232 private ListSortDirection dir;
\r
1234 internal SortableColumn (DataColumn column,
\r
1235 ListSortDirection direction)
\r
1241 public DataColumn Column {
\r
1247 public ListSortDirection SortDirection {
\r
1254 private class RowSorter : IComparer
\r
1256 private SortableColumn[] sortColumns;
\r
1257 private DataRow[] rowsToSort;
\r
1259 internal RowSorter(DataRow[] unsortedRows,
\r
1260 SortableColumn[] sortColumns)
\r
1262 this.sortColumns = sortColumns;
\r
1263 this.rowsToSort = unsortedRows;
\r
1266 public SortableColumn[] SortColumns {
\r
1268 return sortColumns;
\r
1272 public DataRow[] SortRows ()
\r
1274 Array.Sort (rowsToSort, this);
\r
1275 return rowsToSort;
\r
1278 int IComparer.Compare (object x, object y)
\r
1281 throw new Exception ("Object to compare is null: x");
\r
1283 throw new Exception ("Object to compare is null: y");
\r
1284 if(!(x is DataRow))
\r
1285 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
\r
1286 if(!(y is DataRow))
\r
1287 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
\r
1289 DataRow rowx = (DataRow) x;
\r
1290 DataRow rowy = (DataRow) y;
\r
1292 for(int i = 0; i < sortColumns.Length; i++) {
\r
1293 SortableColumn sortColumn = sortColumns[i];
\r
1294 DataColumn dc = sortColumn.Column;
\r
1296 IComparable objx = (IComparable) rowx[dc];
\r
1297 object objy = rowy[dc];
\r
1299 int result = CompareObjects (objx, objy);
\r
1300 if (result != 0) {
\r
1301 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
\r
1312 private int CompareObjects (object a, object b)
\r
1316 else if (a == null)
\r
1318 else if (a == DBNull.Value)
\r
1320 else if (b == null)
\r
1322 else if (b == DBNull.Value)
\r
1325 if((a is string) && (b is string)) {
\r
1326 a = ((string) a).ToUpper ();
\r
1327 b = ((string) b).ToUpper ();
\r
1330 if (a is IComparable)
\r
1331 return ((a as IComparable).CompareTo (b));
\r
1332 else if (b is IComparable)
\r
1333 return -((b as IComparable).CompareTo (a));
\r
1335 throw new ArgumentException ("Neither a nor b IComparable");
\r