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-2003
\r
15 // Copyright (C) Daniel Morgan, 2002-2003
\r
19 using System.Collections;
\r
20 using System.ComponentModel;
\r
21 using System.Globalization;
\r
23 using System.Runtime.Serialization;
\r
26 namespace System.Data {
\r
28 [ToolboxItem (false)]
\r
29 [DefaultEvent ("RowChanging")]
\r
30 [DefaultProperty ("TableName")]
\r
31 [DesignTimeVisible (false)]
\r
33 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
\r
35 internal DataSet dataSet;
\r
37 private bool _caseSensitive;
\r
38 private DataColumnCollection _columnCollection;
\r
39 private ConstraintCollection _constraintCollection;
\r
40 private DataView _defaultView;
\r
42 private string _displayExpression;
\r
43 private PropertyCollection _extendedProperties;
\r
44 private bool _hasErrors;
\r
45 private CultureInfo _locale;
\r
46 private int _minimumCapacity;
\r
47 private string _nameSpace;
\r
48 private DataRelationCollection _childRelations;
\r
49 private DataRelationCollection _parentRelations;
\r
50 private string _prefix;
\r
51 private DataColumn[] _primaryKey;
\r
52 private DataRowCollection _rows;
\r
53 private ISite _site;
\r
54 private string _tableName;
\r
55 private bool _containsListCollection;
\r
56 private string _encodedTableName;
\r
57 internal bool _duringDataLoad;
\r
58 private bool dataSetPrevEnforceConstraints;
\r
62 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
\r
63 // CaseSensitive property. So when you lost you virginity it's gone for ever
\r
64 private bool _virginCaseSensitive = true;
\r
67 /// Initializes a new instance of the DataTable class with no arguments.
\r
70 public DataTable ()
\r
73 _columnCollection = new DataColumnCollection(this);
\r
74 _constraintCollection = new ConstraintCollection(this);
\r
75 _extendedProperties = new PropertyCollection();
\r
78 _caseSensitive = false; //default value
\r
79 _displayExpression = null;
\r
82 _rows = new DataRowCollection (this);
\r
84 //LAMESPEC: spec says 25 impl does 50
\r
85 _minimumCapacity = 50;
\r
87 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
88 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
91 _defaultView = new DataView(this);
\r
95 /// Intitalizes a new instance of the DataTable class with the specified table name.
\r
97 public DataTable (string tableName) : this ()
\r
99 _tableName = tableName;
\r
103 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
\r
106 protected DataTable (SerializationInfo info, StreamingContext context)
\r
110 // TODO: Add constructor logic here
\r
115 public DataTable (string tableName, string tbNamespace)
\r
118 _nameSpace = tbNamespace;
\r
123 /// Indicates whether string comparisons within the table are case-sensitive.
\r
125 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
\r
126 public bool CaseSensitive {
\r
127 get { return _caseSensitive; }
\r
129 _virginCaseSensitive = false;
\r
130 _caseSensitive = value;
\r
134 internal bool VirginCaseSensitive {
\r
135 get { return _virginCaseSensitive; }
\r
136 set { _virginCaseSensitive = value; }
\r
139 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
\r
141 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
142 OnColumnChanged(e);
\r
145 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
\r
147 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
148 OnColumnChanging (e);
\r
151 internal void DeletedDataRow (DataRow dr, DataRowAction action)
\r
153 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
157 internal void DeletingDataRow (DataRow dr, DataRowAction action)
\r
159 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
163 internal void ChangedDataRow (DataRow dr, DataRowAction action)
\r
165 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
169 internal void ChangingDataRow (DataRow dr, DataRowAction action)
\r
171 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
176 /// Gets the collection of child relations for this DataTable.
\r
178 [Browsable (false)]
\r
179 [DataSysDescription ("Returns the child relations for this table.")]
\r
180 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
181 public DataRelationCollection ChildRelations {
\r
183 return _childRelations;
\r
188 /// Gets the collection of columns that belong to this table.
\r
190 [DataCategory ("Data")]
\r
191 [DataSysDescription ("The collection that holds the columns for this table.")]
\r
192 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
193 public DataColumnCollection Columns {
\r
194 get { return _columnCollection; }
\r
198 /// Gets the collection of constraints maintained by this table.
\r
200 [DataCategory ("Data")]
\r
201 [DataSysDescription ("The collection that holds the constraints for this table.")]
\r
202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
203 public ConstraintCollection Constraints {
\r
204 get { return _constraintCollection; }
\r
208 /// Gets the DataSet that this table belongs to.
\r
210 [Browsable (false)]
\r
211 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
\r
212 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
213 public DataSet DataSet {
\r
214 get { return dataSet; }
\r
220 /// Gets a customized view of the table which may
\r
221 /// include a filtered view, or a cursor position.
\r
224 [Browsable (false)]
\r
225 [DataSysDescription ("This is the default DataView for the table.")]
\r
226 public DataView DefaultView {
\r
227 get { return _defaultView; }
\r
232 /// Gets or sets the expression that will return
\r
233 /// a value used to represent this table in the user interface.
\r
235 [DataCategory ("Data")]
\r
236 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
\r
237 [DefaultValue ("")]
\r
238 public string DisplayExpression {
\r
239 get { return "" + _displayExpression; }
\r
240 set { _displayExpression = value; }
\r
244 /// Gets the collection of customized user information.
\r
246 [Browsable (false)]
\r
247 [DataCategory ("Data")]
\r
248 [DataSysDescription ("The collection that holds custom user information.")]
\r
249 public PropertyCollection ExtendedProperties {
\r
250 get { return _extendedProperties; }
\r
254 /// Gets a value indicating whether there are errors in
\r
255 /// any of the_rows in any of the tables of the DataSet to
\r
256 /// which the table belongs.
\r
258 [Browsable (false)]
\r
259 [DataSysDescription ("Returns whether the table has errors.")]
\r
260 public bool HasErrors {
\r
262 // we can not use the _hasError flag because we do not know when to turn it off!
\r
263 for (int i = 0; i < _rows.Count; i++)
\r
265 if (_rows[i].HasErrors)
\r
273 /// Gets or sets the locale information used to
\r
274 /// compare strings within the table.
\r
276 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
\r
277 public CultureInfo Locale {
\r
279 // if the locale is null, we check for the DataSet locale
\r
280 // and if the DataSet is null we return the current culture.
\r
281 // this way if DataSet locale is changed, only if there is no locale for
\r
282 // the DataTable it influece the Locale get;
\r
283 if (_locale != null)
\r
285 if (DataSet != null)
\r
286 return DataSet.Locale;
\r
287 return CultureInfo.CurrentCulture;
\r
290 if (_locale == null || !_locale.Equals(value))
\r
296 /// Gets or sets the initial starting size for this table.
\r
298 [DataCategory ("Data")]
\r
299 [DataSysDescription ("Indicates an initial starting size for this table.")]
\r
300 [DefaultValue (50)]
\r
301 public int MinimumCapacity {
\r
302 get { return _minimumCapacity; }
\r
303 set { _minimumCapacity = value; }
\r
307 /// Gets or sets the namespace for the XML represenation
\r
308 /// of the data stored in the DataTable.
\r
310 [DataCategory ("Data")]
\r
311 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
\r
312 public string Namespace {
\r
313 get { return "" + _nameSpace; }
\r
314 set { _nameSpace = value; }
\r
318 /// Gets the collection of parent relations for
\r
319 /// this DataTable.
\r
321 [Browsable (false)]
\r
322 [DataSysDescription ("Returns the parent relations for this table.")]
\r
323 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
324 public DataRelationCollection ParentRelations {
\r
326 return _parentRelations;
\r
331 /// Gets or sets the namespace for the XML represenation
\r
332 /// of the data stored in the DataTable.
\r
334 [DataCategory ("Data")]
\r
335 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
\r
336 [DefaultValue ("")]
\r
337 public string Prefix {
\r
338 get { return "" + _prefix; }
\r
339 set { _prefix = value; }
\r
343 /// Gets or sets an array of columns that function as
\r
344 /// primary keys for the data table.
\r
346 [DataCategory ("Data")]
\r
347 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
\r
348 public DataColumn[] PrimaryKey {
\r
350 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
\r
351 if (null == uc) return new DataColumn[] {};
\r
355 UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
\r
357 // first check if value is the same as current PK.
\r
358 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
\r
361 // remove PK Constraint
\r
362 if(oldPKConstraint != null)
\r
364 Constraints.Remove(oldPKConstraint);
\r
369 //Does constraint exist for these columns
\r
370 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
\r
371 this.Constraints, (DataColumn[]) value);
\r
373 //if constraint doesn't exist for columns
\r
374 //create new unique primary key constraint
\r
377 foreach (DataColumn Col in (DataColumn[]) value)
\r
380 if (Col.Table == null)
\r
383 if (Columns.IndexOf (Col) < 0)
\r
384 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
\r
388 uc = new UniqueConstraint( (DataColumn[]) value, true);
\r
390 Constraints.Add (uc);
\r
393 { //set existing constraint as the new primary key
\r
394 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
\r
402 /// Gets the collection of_rows that belong to this table.
\r
404 [Browsable (false)]
\r
405 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
\r
406 public DataRowCollection Rows {
\r
407 get { return _rows; }
\r
411 /// Gets or sets an System.ComponentModel.ISite
\r
412 /// for the DataTable.
\r
414 [Browsable (false)]
\r
415 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
416 public override ISite Site {
\r
417 get { return _site; }
\r
418 set { _site = value; }
\r
422 /// Gets or sets the name of the the DataTable.
\r
424 [DataCategory ("Data")]
\r
425 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
\r
426 [DefaultValue ("")]
\r
427 [RefreshProperties (RefreshProperties.All)]
\r
428 public string TableName {
\r
429 get { return "" + _tableName; }
\r
430 set { _tableName = value; }
\r
433 bool IListSource.ContainsListCollection {
\r
435 // the collection is a DataView
\r
441 /// Commits all the changes made to this table since the
\r
442 /// last time AcceptChanges was called.
\r
444 public void AcceptChanges ()
\r
446 //FIXME: Do we need to validate anything here or
\r
447 //try to catch any errors to deal with them?
\r
449 // we do not use foreach because if one of the rows is in Delete state
\r
450 // it will be romeved from Rows and we get an exception.
\r
452 for (int i = 0; i < Rows.Count; )
\r
455 myRow.AcceptChanges();
\r
457 // if the row state is Detached it meens that it was removed from row list (Rows)
\r
458 // so we should not increase 'i'.
\r
459 if (myRow.RowState != DataRowState.Detached)
\r
465 /// Begins the initialization of a DataTable that is used
\r
466 /// on a form or used by another component. The initialization
\r
467 /// occurs at runtime.
\r
469 public virtual void BeginInit ()
\r
474 /// Turns off notifications, index maintenance, and
\r
475 /// constraints while loading data.
\r
478 public void BeginLoadData ()
\r
480 if (!this._duringDataLoad)
\r
482 //duringDataLoad is important to EndLoadData and
\r
483 //for not throwing unexpected exceptions.
\r
484 this._duringDataLoad = true;
\r
486 if (this.dataSet != null)
\r
488 //Saving old Enforce constraints state for later
\r
489 //use in the EndLoadData.
\r
490 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
\r
491 this.dataSet.EnforceConstraints = false;
\r
498 /// Clears the DataTable of all data.
\r
500 public void Clear () {
\r
501 // TODO: thow an exception if any rows that
\r
502 // have enforced child relations
\r
503 // that would result in child rows being orphaned
\r
504 // now we check if any ForeignKeyConstraint is referncing 'table'.
\r
505 if (DataSet != null)
\r
507 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
\r
509 // loop on all tables in dataset
\r
510 while (tableEnumerator.MoveNext())
\r
512 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
\r
513 // loop on all constrains in the current table
\r
514 while (constraintEnumerator.MoveNext())
\r
516 Object o = constraintEnumerator.Current;
\r
517 // we only check ForeignKeyConstraint
\r
518 if (o is ForeignKeyConstraint)
\r
520 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
\r
521 if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
\r
522 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
\r
528 // TODO: throw a NotSupportedException if the DataTable is part
\r
529 // of a DataSet that is binded to an XmlDataDocument
\r
535 /// Clones the structure of the DataTable, including
\r
536 /// all DataTable schemas and constraints.
\r
539 public virtual DataTable Clone ()
\r
541 DataTable Copy = new DataTable ();
\r
543 CopyProperties (Copy);
\r
548 /// Computes the given expression on the current_rows that
\r
549 /// pass the filter criteria.
\r
552 public object Compute (string expression, string filter)
\r
554 // TODO: implement this function based
\r
555 // on Select (expression)
\r
557 // expression is an aggregate function
\r
558 // filter is an expression used to limit rows
\r
562 DataRow[] rows = Select(filter);
\r
564 ExpressionAggregate Expression = new ExpressionAggregate (expression);
\r
565 obj = Expression.Result(rows);
\r
571 /// Copies both the structure and data for this DataTable.
\r
574 public DataTable Copy ()
\r
576 DataTable Copy = new DataTable ();
\r
577 CopyProperties (Copy);
\r
579 // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
\r
580 // because if the row state is deleted we get an exception.
\r
581 Copy._duringDataLoad = true;
\r
582 foreach (DataRow Row in Rows) {
\r
583 DataRow NewRow = Copy.NewRow ();
\r
584 Copy.Rows.Add (NewRow);
\r
585 Row.CopyValuesToRow(NewRow);
\r
587 Copy._duringDataLoad = false;
\r
592 private void CopyProperties (DataTable Copy)
\r
595 Copy.CaseSensitive = CaseSensitive;
\r
596 Copy.VirginCaseSensitive = VirginCaseSensitive;
\r
598 // Copy.ChildRelations
\r
599 // Copy.Constraints
\r
601 // Copy.DefaultView
\r
603 Copy.DisplayExpression = DisplayExpression;
\r
604 // Copy.ExtendedProperties
\r
605 Copy.Locale = Locale;
\r
606 Copy.MinimumCapacity = MinimumCapacity;
\r
607 Copy.Namespace = Namespace;
\r
608 // Copy.ParentRelations
\r
609 Copy.Prefix = Prefix;
\r
611 Copy.TableName = TableName;
\r
616 foreach (DataColumn Column in Columns) {
\r
618 Copy.Columns.Add (CopyColumn (Column));
\r
621 CopyConstraints(Copy);
\r
622 // add primary key to the copy
\r
623 if (PrimaryKey.Length > 0)
\r
625 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
\r
626 for (int i = 0; i < pColumns.Length; i++)
\r
627 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
\r
629 Copy.PrimaryKey = pColumns;
\r
633 private void CopyConstraints(DataTable copy)
\r
635 UniqueConstraint origUc;
\r
636 UniqueConstraint copyUc;
\r
637 for (int i = 0; i < this.Constraints.Count; i++)
\r
639 if (this.Constraints[i] is UniqueConstraint)
\r
641 origUc = (UniqueConstraint)this.Constraints[i];
\r
642 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
\r
643 for (int j = 0; j < columns.Length; j++)
\r
644 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
\r
646 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
\r
647 copy.Constraints.Add(copyUc);
\r
652 /// Ends the initialization of a DataTable that is used
\r
653 /// on a form or used by another component. The
\r
654 /// initialization occurs at runtime.
\r
657 public virtual void EndInit ()
\r
663 /// Turns on notifications, index maintenance, and
\r
664 /// constraints after loading data.
\r
667 public void EndLoadData()
\r
670 if (this._duringDataLoad)
\r
672 //Returning from loading mode, raising exceptions as usual
\r
673 this._duringDataLoad = false;
\r
675 if (this.dataSet !=null)
\r
677 //Getting back to previous EnforceConstraint state
\r
678 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
\r
680 for (i=0 ; i<this.Rows.Count ; i++)
\r
682 if (this.Rows[i]._nullConstraintViolation )
\r
684 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
\r
694 /// Gets a copy of the DataTable that contains all
\r
695 /// changes made to it since it was loaded or
\r
696 /// AcceptChanges was last called.
\r
699 public DataTable GetChanges()
\r
701 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
\r
705 /// Gets a copy of the DataTable containing all
\r
706 /// changes made to it since it was last loaded, or
\r
707 /// since AcceptChanges was called, filtered by DataRowState.
\r
710 public DataTable GetChanges(DataRowState rowStates)
\r
712 DataTable copyTable = null;
\r
714 IEnumerator rowEnumerator = Rows.GetEnumerator();
\r
715 while (rowEnumerator.MoveNext()) {
\r
716 DataRow row = (DataRow)rowEnumerator.Current;
\r
717 if (row.IsRowChanged(rowStates)) {
\r
718 if (copyTable == null)
\r
719 copyTable = Clone();
\r
720 DataRow newRow = copyTable.NewRow();
\r
721 copyTable.Rows.Add(newRow);
\r
722 row.CopyValuesToRow(newRow);
\r
731 public DataTableReader GetDataReader ()
\r
733 throw new NotImplementedException ();
\r
738 /// Gets an array of DataRow objects that contain errors.
\r
741 public DataRow[] GetErrors ()
\r
743 ArrayList errors = new ArrayList();
\r
744 for (int i = 0; i < _rows.Count; i++)
\r
746 if (_rows[i].HasErrors)
\r
747 errors.Add(_rows[i]);
\r
750 return (DataRow[]) errors.ToArray(typeof(DataRow));
\r
754 /// This member is only meant to support Mono's infrastructure
\r
756 protected virtual DataTable CreateInstance ()
\r
758 return Activator.CreateInstance (this.GetType (), true) as DataTable;
\r
762 /// This member is only meant to support Mono's infrastructure
\r
764 protected virtual Type GetRowType ()
\r
766 return typeof (DataRow);
\r
770 /// This member is only meant to support Mono's infrastructure
\r
772 /// Used for Data Binding between System.Web.UI. controls
\r
773 /// like a DataGrid
\r
775 /// System.Windows.Forms controls like a DataGrid
\r
777 IList IListSource.GetList ()
\r
779 IList list = (IList) _defaultView;
\r
784 /// Copies a DataRow into a DataTable, preserving any
\r
785 /// property settings, as well as original and current values.
\r
788 public void ImportRow (DataRow row)
\r
790 DataRow newRow = NewRow();
\r
792 row.CopyValuesToRow(newRow);
\r
797 /// This member is only meant to support Mono's infrastructure
\r
800 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
\r
806 public void Load (IDataReader reader)
\r
808 throw new NotImplementedException ();
\r
812 public void Load (IDataReader reader, LoadOption loadOption)
\r
814 throw new NotImplementedException ();
\r
819 /// Finds and updates a specific row. If no matching row
\r
820 /// is found, a new row is created using the given values.
\r
823 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
\r
825 DataRow row = null;
\r
826 if (PrimaryKey.Length == 0) {
\r
827 row = Rows.Add (values);
\r
828 if (fAcceptChanges)
\r
829 row.AcceptChanges ();
\r
832 bool hasPrimaryValues = true;
\r
833 // initiate an array that has the values of the primary keys.
\r
834 object[] keyValues = new object[PrimaryKey.Length];
\r
835 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
\r
837 if(PrimaryKey[i].Ordinal < values.Length)
\r
838 keyValues[i] = values[PrimaryKey[i].Ordinal];
\r
840 hasPrimaryValues = false;
\r
843 if (hasPrimaryValues){
\r
844 // find the row in the table.
\r
845 row = Rows.Find(keyValues);
\r
849 row = Rows.Add (values);
\r
851 row.ItemArray = values;
\r
853 if (fAcceptChanges)
\r
854 row.AcceptChanges ();
\r
862 public DataRow LoadDataRow (object[] values, LoadOption loadOption)
\r
864 throw new NotImplementedException ();
\r
868 public void Merge (DataTable table)
\r
870 throw new NotImplementedException ();
\r
874 public void Merge (DataTable table, bool preserveChanges)
\r
876 throw new NotImplementedException ();
\r
880 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
\r
882 throw new NotImplementedException ();
\r
887 /// Creates a new DataRow with the same schema as the table.
\r
889 public DataRow NewRow ()
\r
891 return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
\r
895 /// This member supports the .NET Framework infrastructure
\r
896 /// and is not intended to be used directly from your code.
\r
898 protected internal DataRow[] NewRowArray (int size)
\r
900 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
\r
904 /// Creates a new row from an existing row.
\r
906 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
908 return new DataRow (builder);
\r
913 XmlReadMode ReadXml (Stream stream)
\r
915 throw new NotImplementedException ();
\r
919 public void ReadXmlSchema (Stream stream)
\r
921 throw new NotImplementedException ();
\r
925 public void ReadXmlSchema (TextReader reader)
\r
927 throw new NotImplementedException ();
\r
931 public void ReadXmlSchema (string fileName)
\r
933 throw new NotImplementedException ();
\r
937 public void ReadXmlSchema (XmlReader reader)
\r
939 throw new NotImplementedException ();
\r
944 /// Rolls back all changes that have been made to the
\r
945 /// table since it was loaded, or the last time AcceptChanges
\r
949 public void RejectChanges ()
\r
951 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
952 DataRow row = _rows [i];
\r
953 if (row.RowState != DataRowState.Unchanged)
\r
954 _rows [i].RejectChanges ();
\r
959 /// Resets the DataTable to its original state.
\r
962 public virtual void Reset ()
\r
965 while (ParentRelations.Count > 0)
\r
967 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
\r
968 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
\r
971 while (ChildRelations.Count > 0)
\r
973 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
\r
974 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
\r
976 Constraints.Clear();
\r
981 /// Gets an array of all DataRow objects.
\r
983 public DataRow[] Select ()
\r
985 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
989 /// Gets an array of all DataRow objects that match
\r
990 /// the filter criteria in order of primary key (or
\r
991 /// lacking one, order of addition.)
\r
993 public DataRow[] Select (string filterExpression)
\r
995 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
999 /// Gets an array of all DataRow objects that
\r
1000 /// match the filter criteria, in the the
\r
1001 /// specified sort order.
\r
1003 public DataRow[] Select (string filterExpression, string sort)
\r
1005 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
\r
1009 /// Gets an array of all DataRow objects that match
\r
1010 /// the filter in the order of the sort, that match
\r
1011 /// the specified state.
\r
1014 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
\r
1016 DataRow[] dataRows = null;
\r
1017 if (filterExpression == null)
\r
1018 filterExpression = String.Empty;
\r
1020 ExpressionElement Expression = null;
\r
1021 if (filterExpression != null && filterExpression.Length != 0)
\r
1022 Expression = new ExpressionMainElement(filterExpression);
\r
1024 ArrayList List = new ArrayList();
\r
1025 int recordStateFilter = GetRowStateFilter(recordStates);
\r
1026 foreach (DataRow Row in Rows)
\r
1028 if (((int)Row.RowState & recordStateFilter) != 0)
\r
1030 if (Expression == null || Expression.Test(Row))
\r
1035 dataRows = (DataRow[])List.ToArray(typeof(DataRow));
\r
1038 if (sort != null && !sort.Equals(String.Empty))
\r
1040 SortableColumn[] sortableColumns = null;
\r
1042 sortableColumns = ParseTheSortString (sort);
\r
1043 if (sortableColumns == null)
\r
1044 throw new Exception ("sort expression result is null");
\r
1045 if (sortableColumns.Length == 0)
\r
1046 throw new Exception("sort expression result is 0");
\r
1048 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
\r
1049 dataRows = rowSorter.SortRows ();
\r
1051 sortableColumns = null;
\r
1059 private static int GetRowStateFilter(DataViewRowState recordStates)
\r
1063 if ((recordStates & DataViewRowState.Added) != 0)
\r
1064 flag |= (int)DataRowState.Added;
\r
1065 if ((recordStates & DataViewRowState.Deleted) != 0)
\r
1066 flag |= (int)DataRowState.Deleted;
\r
1067 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
\r
1068 flag |= (int)DataRowState.Modified;
\r
1069 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
\r
1070 flag |= (int)DataRowState.Modified;
\r
1071 if ((recordStates & DataViewRowState.Unchanged) != 0)
\r
1072 flag |= (int)DataRowState.Unchanged;
\r
1078 /// Gets the TableName and DisplayExpression, if
\r
1079 /// there is one as a concatenated string.
\r
1081 public override string ToString()
\r
1083 //LAMESPEC: spec says concat the two. impl puts a
\r
1084 //plus sign infront of DisplayExpression
\r
1085 string retVal = TableName;
\r
1086 if(DisplayExpression != null && DisplayExpression != "")
\r
1087 retVal += " + " + DisplayExpression;
\r
1093 public void WriteXml (Stream stream)
\r
1095 throw new NotImplementedException ();
\r
1099 public void WriteXml (TextWriter writer)
\r
1101 throw new NotImplementedException ();
\r
1105 public void WriteXml (XmlWriter writer)
\r
1107 throw new NotImplementedException ();
\r
1111 public void WriteXml (string fileName)
\r
1113 throw new NotImplementedException ();
\r
1117 public void WriteXml (Stream stream, XmlWriteMode mode)
\r
1119 throw new NotImplementedException ();
\r
1123 public void WriteXml (TextWriter writer, XmlWriteMode mode)
\r
1125 throw new NotImplementedException ();
\r
1129 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
\r
1131 throw new NotImplementedException ();
\r
1135 public void WriteXml (string fileName, XmlWriteMode mode)
\r
1137 throw new NotImplementedException ();
\r
1141 public void WriteXmlSchema (Stream stream)
\r
1143 throw new NotImplementedException ();
\r
1147 public void WriteXmlSchema (TextWriter writer)
\r
1149 throw new NotImplementedException ();
\r
1153 public void WriteXmlSchema (XmlWriter writer)
\r
1155 throw new NotImplementedException ();
\r
1159 public void WriteXmlSchema (string fileName)
\r
1161 throw new NotImplementedException ();
\r
1165 #region Events /////////////////
\r
1168 /// Raises the ColumnChanged event.
\r
1170 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
\r
1171 if (null != ColumnChanged) {
\r
1172 ColumnChanged (this, e);
\r
1177 /// Raises the ColumnChanging event.
\r
1179 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
\r
1180 if (null != ColumnChanging) {
\r
1181 ColumnChanging (this, e);
\r
1186 /// Raises the PropertyChanging event.
\r
1189 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
\r
1190 // if (null != PropertyChanging)
\r
1192 // PropertyChanging (this, e);
\r
1197 /// Notifies the DataTable that a DataColumn is being removed.
\r
1200 protected internal virtual void OnRemoveColumn (DataColumn column) {
\r
1201 // if (null != RemoveColumn)
\r
1203 // RemoveColumn(this, e);
\r
1208 /// Raises the RowChanged event.
\r
1210 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
\r
1211 if (null != RowChanged) {
\r
1212 RowChanged(this, e);
\r
1218 /// Raises the RowChanging event.
\r
1220 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
\r
1221 if (null != RowChanging) {
\r
1222 RowChanging(this, e);
\r
1227 /// Raises the RowDeleted event.
\r
1229 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
\r
1230 if (null != RowDeleted) {
\r
1231 RowDeleted(this, e);
\r
1236 /// Raises the RowDeleting event.
\r
1238 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
\r
1239 if (null != RowDeleting) {
\r
1240 RowDeleting(this, e);
\r
1245 private DataColumn CopyColumn (DataColumn Column) {
\r
1246 DataColumn Copy = new DataColumn ();
\r
1248 // Copy all the properties of column
\r
1249 Copy.AllowDBNull = Column.AllowDBNull;
\r
1250 Copy.AutoIncrement = Column.AutoIncrement;
\r
1251 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
\r
1252 Copy.AutoIncrementStep = Column.AutoIncrementStep;
\r
1253 Copy.Caption = Column.Caption;
\r
1254 Copy.ColumnMapping = Column.ColumnMapping;
\r
1255 Copy.ColumnName = Column.ColumnName;
\r
1257 Copy.DataType = Column.DataType;
\r
1258 Copy.DefaultValue = Column.DefaultValue;
\r
1259 Copy.Expression = Column.Expression;
\r
1260 //Copy.ExtendedProperties
\r
1261 Copy.MaxLength = Column.MaxLength;
\r
1262 Copy.Namespace = Column.Namespace;
\r
1263 Copy.Prefix = Column.Prefix;
\r
1264 Copy.ReadOnly = Column.ReadOnly;
\r
1266 //we do not copy the unique value - it will be copyied when copying the constraints.
\r
1267 //Copy.Unique = Column.Unique;
\r
1273 /// Occurs when after a value has been changed for
\r
1274 /// the specified DataColumn in a DataRow.
\r
1276 [DataCategory ("Data")]
\r
1277 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1278 public event DataColumnChangeEventHandler ColumnChanged;
\r
1281 /// Occurs when a value is being changed for the specified
\r
1282 /// DataColumn in a DataRow.
\r
1284 [DataCategory ("Data")]
\r
1285 [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
1286 public event DataColumnChangeEventHandler ColumnChanging;
\r
1289 /// Occurs after a DataRow has been changed successfully.
\r
1291 [DataCategory ("Data")]
\r
1292 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1293 public event DataRowChangeEventHandler RowChanged;
\r
1296 /// Occurs when a DataRow is changing.
\r
1298 [DataCategory ("Data")]
\r
1299 [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
1300 public event DataRowChangeEventHandler RowChanging;
\r
1303 /// Occurs after a row in the table has been deleted.
\r
1305 [DataCategory ("Data")]
\r
1306 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1307 public event DataRowChangeEventHandler RowDeleted;
\r
1310 /// Occurs before a row in the table is about to be deleted.
\r
1312 [DataCategory ("Data")]
\r
1313 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1314 public event DataRowChangeEventHandler RowDeleting;
\r
1316 #endregion // Events
\r
1319 /// Removes all UniqueConstraints
\r
1321 private void RemoveUniqueConstraints ()
\r
1323 foreach (Constraint Cons in Constraints) {
\r
1325 if (Cons is UniqueConstraint) {
\r
1326 Constraints.Remove (Cons);
\r
1331 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
\r
1334 // to parse the sort string for DataTable:Select(expression,sort)
\r
1335 // into sortable columns (think ORDER BY,
\r
1336 // such as, "customer ASC, price DESC" )
\r
1337 private SortableColumn[] ParseTheSortString (string sort)
\r
1339 SortableColumn[] sortColumns = null;
\r
1340 ArrayList columns = null;
\r
1342 if (sort != null && !sort.Equals ("")) {
\r
1343 columns = new ArrayList ();
\r
1344 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1346 for (int c = 0; c < columnExpression.Length; c++) {
\r
1347 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
1349 string columnName = columnSortInfo[0].Trim ();
\r
1350 string sortOrder = "ASC";
\r
1351 if (columnSortInfo.Length > 1)
\r
1352 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
\r
1354 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
1355 switch (sortOrder) {
\r
1357 sortDirection = ListSortDirection.Ascending;
\r
1360 sortDirection = ListSortDirection.Descending;
\r
1363 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
1367 ord = Int32.Parse (columnName);
\r
1369 catch (FormatException) {
\r
1372 DataColumn dc = null;
\r
1374 dc = _columnCollection[columnName];
\r
1376 dc = _columnCollection[ord];
\r
1377 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
\r
1378 columns.Add (sortCol);
\r
1380 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
\r
1382 return sortColumns;
\r
1385 private class SortableColumn
\r
1387 private DataColumn col;
\r
1388 private ListSortDirection dir;
\r
1390 internal SortableColumn (DataColumn column,
\r
1391 ListSortDirection direction)
\r
1397 public DataColumn Column {
\r
1403 public ListSortDirection SortDirection {
\r
1410 private class RowSorter : IComparer
\r
1412 private SortableColumn[] sortColumns;
\r
1413 private DataRow[] rowsToSort;
\r
1415 internal RowSorter(DataRow[] unsortedRows,
\r
1416 SortableColumn[] sortColumns)
\r
1418 this.sortColumns = sortColumns;
\r
1419 this.rowsToSort = unsortedRows;
\r
1422 public SortableColumn[] SortColumns {
\r
1424 return sortColumns;
\r
1428 public DataRow[] SortRows ()
\r
1430 Array.Sort (rowsToSort, this);
\r
1431 return rowsToSort;
\r
1434 int IComparer.Compare (object x, object y)
\r
1437 throw new Exception ("Object to compare is null: x");
\r
1439 throw new Exception ("Object to compare is null: y");
\r
1440 if(!(x is DataRow))
\r
1441 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
\r
1442 if(!(y is DataRow))
\r
1443 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
\r
1445 DataRow rowx = (DataRow) x;
\r
1446 DataRow rowy = (DataRow) y;
\r
1448 for(int i = 0; i < sortColumns.Length; i++) {
\r
1449 SortableColumn sortColumn = sortColumns[i];
\r
1450 DataColumn dc = sortColumn.Column;
\r
1452 IComparable objx = (IComparable) rowx[dc];
\r
1453 object objy = rowy[dc];
\r
1455 int result = CompareObjects (objx, objy);
\r
1456 if (result != 0) {
\r
1457 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
\r
1468 private int CompareObjects (object a, object b)
\r
1472 else if (a == null)
\r
1474 else if (a == DBNull.Value)
\r
1476 else if (b == null)
\r
1478 else if (b == DBNull.Value)
\r
1481 if((a is string) && (b is string)) {
\r
1482 a = ((string) a).ToUpper ();
\r
1483 b = ((string) b).ToUpper ();
\r
1486 if (a is IComparable)
\r
1487 return ((a as IComparable).CompareTo (b));
\r
1488 else if (b is IComparable)
\r
1489 return -((b as IComparable).CompareTo (a));
\r
1491 throw new ArgumentException ("Neither a nor b IComparable");
\r