2 // System.Data.DataTable.cs
5 // Franklin Wise <gracenote@earthlink.net>
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Daniel Morgan <danmorg@sc.rr.com>
8 // Rodrigo Moya <rodrigo@ximian.com>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002-2003
15 // Copyright (C) Daniel Morgan, 2002-2003
19 using System.Collections;
20 using System.ComponentModel;
21 using System.Globalization;
23 using System.Runtime.Serialization;
25 using System.Xml.Schema;
27 namespace System.Data {
30 [DefaultEvent ("RowChanging")]
31 [DefaultProperty ("TableName")]
32 [DesignTimeVisible (false)]
34 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
36 internal DataSet dataSet;
38 private bool _caseSensitive;
39 private DataColumnCollection _columnCollection;
40 private ConstraintCollection _constraintCollection;
41 private DataView _defaultView;
43 private string _displayExpression;
44 private PropertyCollection _extendedProperties;
45 private bool _hasErrors;
46 private CultureInfo _locale;
47 private int _minimumCapacity;
48 private string _nameSpace;
49 private DataRelationCollection _childRelations;
50 private DataRelationCollection _parentRelations;
51 private string _prefix;
52 private DataColumn[] _primaryKey;
53 private DataRowCollection _rows;
55 private string _tableName;
56 private bool _containsListCollection;
57 private string _encodedTableName;
58 internal bool _duringDataLoad;
59 private bool dataSetPrevEnforceConstraints;
60 private DataRowBuilder _rowBuilder;
61 private ArrayList _indexes;
64 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
65 // CaseSensitive property. So when you lost you virginity it's gone for ever
66 private bool _virginCaseSensitive = true;
69 /// Initializes a new instance of the DataTable class with no arguments.
75 _columnCollection = new DataColumnCollection(this);
76 _constraintCollection = new ConstraintCollection(this);
77 _extendedProperties = new PropertyCollection();
80 _caseSensitive = false; //default value
81 _displayExpression = null;
84 _rows = new DataRowCollection (this);
85 _indexes = new ArrayList();
87 //LAMESPEC: spec says 25 impl does 50
88 _minimumCapacity = 50;
90 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
91 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
94 _defaultView = new DataView(this);
98 /// Intitalizes a new instance of the DataTable class with the specified table name.
100 public DataTable (string tableName) : this ()
102 _tableName = tableName;
106 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
109 protected DataTable (SerializationInfo info, StreamingContext context)
112 string schema = info.GetString ("XmlSchema");
113 string data = info.GetString ("XmlDiffGram");
115 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
116 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
119 XmlDiffLoader loader = new XmlDiffLoader (this);
120 xtr = new XmlTextReader(new StringReader (data));
125 public DataTable (string tableName, string tbNamespace)
128 _nameSpace = tbNamespace;
133 /// Indicates whether string comparisons within the table are case-sensitive.
135 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
136 public bool CaseSensitive {
137 get { return _caseSensitive; }
139 _virginCaseSensitive = false;
140 _caseSensitive = value;
144 internal bool VirginCaseSensitive {
145 get { return _virginCaseSensitive; }
146 set { _virginCaseSensitive = value; }
149 internal ArrayList Indexes{
150 get { return _indexes; }
153 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
155 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
159 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
161 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
162 OnColumnChanging (e);
165 internal void DeletedDataRow (DataRow dr, DataRowAction action)
167 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
171 internal void DeletingDataRow (DataRow dr, DataRowAction action)
173 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
177 internal void ChangedDataRow (DataRow dr, DataRowAction action)
179 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
183 internal void ChangingDataRow (DataRow dr, DataRowAction action)
185 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
190 /// Gets the collection of child relations for this DataTable.
193 [DataSysDescription ("Returns the child relations for this table.")]
194 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
195 public DataRelationCollection ChildRelations {
197 return _childRelations;
202 /// Gets the collection of columns that belong to this table.
204 [DataCategory ("Data")]
205 [DataSysDescription ("The collection that holds the columns for this table.")]
206 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
207 public DataColumnCollection Columns {
208 get { return _columnCollection; }
212 /// Gets the collection of constraints maintained by this table.
214 [DataCategory ("Data")]
215 [DataSysDescription ("The collection that holds the constraints for this table.")]
216 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
217 public ConstraintCollection Constraints {
218 get { return _constraintCollection; }
222 /// Gets the DataSet that this table belongs to.
225 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
226 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
227 public DataSet DataSet {
228 get { return dataSet; }
234 /// Gets a customized view of the table which may
235 /// include a filtered view, or a cursor position.
239 [DataSysDescription ("This is the default DataView for the table.")]
240 public DataView DefaultView {
241 get { return _defaultView; }
246 /// Gets or sets the expression that will return
247 /// a value used to represent this table in the user interface.
249 [DataCategory ("Data")]
250 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
252 public string DisplayExpression {
253 get { return "" + _displayExpression; }
254 set { _displayExpression = value; }
258 /// Gets the collection of customized user information.
261 [DataCategory ("Data")]
262 [DataSysDescription ("The collection that holds custom user information.")]
263 public PropertyCollection ExtendedProperties {
264 get { return _extendedProperties; }
268 /// Gets a value indicating whether there are errors in
269 /// any of the_rows in any of the tables of the DataSet to
270 /// which the table belongs.
273 [DataSysDescription ("Returns whether the table has errors.")]
274 public bool HasErrors {
276 // we can not use the _hasError flag because we do not know when to turn it off!
277 for (int i = 0; i < _rows.Count; i++)
279 if (_rows[i].HasErrors)
287 /// Gets or sets the locale information used to
288 /// compare strings within the table.
290 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
291 public CultureInfo Locale {
293 // if the locale is null, we check for the DataSet locale
294 // and if the DataSet is null we return the current culture.
295 // this way if DataSet locale is changed, only if there is no locale for
296 // the DataTable it influece the Locale get;
300 return DataSet.Locale;
301 return CultureInfo.CurrentCulture;
304 if (_locale == null || !_locale.Equals(value))
310 /// Gets or sets the initial starting size for this table.
312 [DataCategory ("Data")]
313 [DataSysDescription ("Indicates an initial starting size for this table.")]
315 public int MinimumCapacity {
316 get { return _minimumCapacity; }
317 set { _minimumCapacity = value; }
321 /// Gets or sets the namespace for the XML represenation
322 /// of the data stored in the DataTable.
324 [DataCategory ("Data")]
325 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
326 public string Namespace {
327 get { return "" + _nameSpace; }
328 set { _nameSpace = value; }
332 /// Gets the collection of parent relations for
336 [DataSysDescription ("Returns the parent relations for this table.")]
337 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
338 public DataRelationCollection ParentRelations {
340 return _parentRelations;
345 /// Gets or sets the namespace for the XML represenation
346 /// of the data stored in the DataTable.
348 [DataCategory ("Data")]
349 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
351 public string Prefix {
352 get { return "" + _prefix; }
353 set { _prefix = value; }
357 /// Gets or sets an array of columns that function as
358 /// primary keys for the data table.
360 [DataCategory ("Data")]
361 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
362 public DataColumn[] PrimaryKey {
364 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
365 if (null == uc) return new DataColumn[] {};
369 UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
371 // first check if value is the same as current PK.
372 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
375 // remove PK Constraint
376 if(oldPKConstraint != null) {
377 Constraints.Remove(oldPKConstraint);
381 //Does constraint exist for these columns
382 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
383 this.Constraints, (DataColumn[]) value);
385 //if constraint doesn't exist for columns
386 //create new unique primary key constraint
388 foreach (DataColumn Col in (DataColumn[]) value) {
390 if (Col.Table == null)
393 if (Columns.IndexOf (Col) < 0)
394 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
398 uc = new UniqueConstraint( (DataColumn[]) value, true);
400 Constraints.Add (uc);
403 //set existing constraint as the new primary key
404 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
407 // if we really supplied some columns fo primary key -
408 // rebuild indexes fo all foreign key constraints
409 if(value.Length > 0) {
410 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
411 constraint.AssertConstraint();
416 // if primary key is null now - drop all the indexes for foreign key constraints
417 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
418 Index index = constraint.Index;
419 constraint.Index = null;
420 this.DropIndex(index);
428 /// Gets the collection of_rows that belong to this table.
431 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
432 public DataRowCollection Rows {
433 get { return _rows; }
437 /// Gets or sets an System.ComponentModel.ISite
438 /// for the DataTable.
441 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
442 public override ISite Site {
443 get { return _site; }
444 set { _site = value; }
448 /// Gets or sets the name of the the DataTable.
450 [DataCategory ("Data")]
451 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
453 [RefreshProperties (RefreshProperties.All)]
454 public string TableName {
455 get { return "" + _tableName; }
456 set { _tableName = value; }
459 bool IListSource.ContainsListCollection {
461 // the collection is a DataView
466 public bool RowsExist(Object[] columns, Object[] relatedColumns,DataRow row)
468 object[] vals = new object[relatedColumns.Length];
469 for (int i = 0; i < vals.Length; i++)
470 vals[i] = row[(DataColumn)relatedColumns[i]];
472 return RowsExist(columns,vals);
475 public bool RowsExist(Object[] columns,Object[] values)
477 bool rowsExist = false;
478 Index indx = this.GetIndexByColumns ((DataColumn[])columns);
480 if (indx != null) { // lookup for a row in index
481 rowsExist = (indx.FindSimple (values, false) != null);
482 } else { // no index we have to perform full-table scan
483 // check that there is a parent for this row.
484 foreach (DataRow thisRow in this.Rows) {
485 if (thisRow.RowState != DataRowState.Deleted) {
487 // check if the values in the columns are equal
488 for (int i = 0; i < columns.Length; i++) {
489 if (!thisRow[(DataColumn)columns[i]].Equals(values[i])) {
494 if (match) {// there is a row with columns values equals to those supplied.
505 /// Commits all the changes made to this table since the
506 /// last time AcceptChanges was called.
508 public void AcceptChanges ()
510 //FIXME: Do we need to validate anything here or
511 //try to catch any errors to deal with them?
513 // we do not use foreach because if one of the rows is in Delete state
514 // it will be romeved from Rows and we get an exception.
516 for (int i = 0; i < Rows.Count; )
519 myRow.AcceptChanges();
521 // if the row state is Detached it meens that it was removed from row list (Rows)
522 // so we should not increase 'i'.
523 if (myRow.RowState != DataRowState.Detached)
529 /// Begins the initialization of a DataTable that is used
530 /// on a form or used by another component. The initialization
531 /// occurs at runtime.
533 public virtual void BeginInit ()
538 /// Turns off notifications, index maintenance, and
539 /// constraints while loading data.
542 public void BeginLoadData ()
544 if (!this._duringDataLoad)
546 //duringDataLoad is important to EndLoadData and
547 //for not throwing unexpected exceptions.
548 this._duringDataLoad = true;
550 if (this.dataSet != null)
552 //Saving old Enforce constraints state for later
553 //use in the EndLoadData.
554 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
555 this.dataSet.EnforceConstraints = false;
562 /// Clears the DataTable of all data.
564 public void Clear () {
565 // TODO: thow an exception if any rows that
566 // have enforced child relations
567 // that would result in child rows being orphaned
568 // now we check if any ForeignKeyConstraint is referncing 'table'.
571 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
573 // loop on all tables in dataset
574 while (tableEnumerator.MoveNext())
576 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
577 // loop on all constrains in the current table
578 while (constraintEnumerator.MoveNext())
580 Object o = constraintEnumerator.Current;
581 // we only check ForeignKeyConstraint
582 if (o is ForeignKeyConstraint)
584 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
585 if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
586 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
592 // TODO: throw a NotSupportedException if the DataTable is part
593 // of a DataSet that is binded to an XmlDataDocument
599 /// Clones the structure of the DataTable, including
600 /// all DataTable schemas and constraints.
603 public virtual DataTable Clone ()
605 DataTable Copy = new DataTable ();
607 CopyProperties (Copy);
612 /// Computes the given expression on the current_rows that
613 /// pass the filter criteria.
616 public object Compute (string expression, string filter)
618 // TODO: implement this function based
619 // on Select (expression)
621 // expression is an aggregate function
622 // filter is an expression used to limit rows
626 DataRow[] rows = Select(filter);
628 ExpressionAggregate Expression = new ExpressionAggregate (expression);
629 obj = Expression.Result(rows);
635 /// Copies both the structure and data for this DataTable.
638 public DataTable Copy ()
640 DataTable Copy = new DataTable ();
641 CopyProperties (Copy);
643 // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
644 // because if the row state is deleted we get an exception.
645 Copy._duringDataLoad = true;
646 foreach (DataRow Row in Rows) {
647 DataRow NewRow = Copy.NewRow ();
648 Copy.Rows.Add (NewRow);
649 Row.CopyValuesToRow(NewRow);
651 Copy._duringDataLoad = false;
656 private void CopyProperties (DataTable Copy)
659 Copy.CaseSensitive = CaseSensitive;
660 Copy.VirginCaseSensitive = VirginCaseSensitive;
662 // Copy.ChildRelations
667 Copy.DisplayExpression = DisplayExpression;
668 // Copy.ExtendedProperties
669 Copy.Locale = Locale;
670 Copy.MinimumCapacity = MinimumCapacity;
671 Copy.Namespace = Namespace;
672 // Copy.ParentRelations
673 Copy.Prefix = Prefix;
675 Copy.TableName = TableName;
680 foreach (DataColumn Column in Columns) {
682 Copy.Columns.Add (CopyColumn (Column));
685 CopyConstraints(Copy);
686 // add primary key to the copy
687 if (PrimaryKey.Length > 0)
689 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
690 for (int i = 0; i < pColumns.Length; i++)
691 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
693 Copy.PrimaryKey = pColumns;
697 private void CopyConstraints(DataTable copy)
699 UniqueConstraint origUc;
700 UniqueConstraint copyUc;
701 for (int i = 0; i < this.Constraints.Count; i++)
703 if (this.Constraints[i] is UniqueConstraint)
705 origUc = (UniqueConstraint)this.Constraints[i];
706 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
707 for (int j = 0; j < columns.Length; j++)
708 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
710 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
711 copy.Constraints.Add(copyUc);
716 /// Ends the initialization of a DataTable that is used
717 /// on a form or used by another component. The
718 /// initialization occurs at runtime.
721 public virtual void EndInit ()
727 /// Turns on notifications, index maintenance, and
728 /// constraints after loading data.
731 public void EndLoadData()
734 if (this._duringDataLoad)
736 //Returning from loading mode, raising exceptions as usual
737 this._duringDataLoad = false;
739 if (this.dataSet !=null)
741 //Getting back to previous EnforceConstraint state
742 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
744 for (i=0 ; i<this.Rows.Count ; i++)
746 if (this.Rows[i]._nullConstraintViolation )
748 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
758 /// Gets a copy of the DataTable that contains all
759 /// changes made to it since it was loaded or
760 /// AcceptChanges was last called.
763 public DataTable GetChanges()
765 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
769 /// Gets a copy of the DataTable containing all
770 /// changes made to it since it was last loaded, or
771 /// since AcceptChanges was called, filtered by DataRowState.
774 public DataTable GetChanges(DataRowState rowStates)
776 DataTable copyTable = null;
778 IEnumerator rowEnumerator = Rows.GetEnumerator();
779 while (rowEnumerator.MoveNext()) {
780 DataRow row = (DataRow)rowEnumerator.Current;
781 if (row.IsRowChanged(rowStates)) {
782 if (copyTable == null)
784 DataRow newRow = copyTable.NewRow();
785 copyTable.Rows.Add(newRow);
786 row.CopyValuesToRow(newRow);
795 public DataTableReader GetDataReader ()
797 throw new NotImplementedException ();
802 /// Gets an array of DataRow objects that contain errors.
805 public DataRow[] GetErrors ()
807 ArrayList errors = new ArrayList();
808 for (int i = 0; i < _rows.Count; i++)
810 if (_rows[i].HasErrors)
811 errors.Add(_rows[i]);
814 return (DataRow[]) errors.ToArray(typeof(DataRow));
818 /// This member is only meant to support Mono's infrastructure
820 protected virtual DataTable CreateInstance ()
822 return Activator.CreateInstance (this.GetType (), true) as DataTable;
826 /// This member is only meant to support Mono's infrastructure
828 protected virtual Type GetRowType ()
830 return typeof (DataRow);
834 /// This member is only meant to support Mono's infrastructure
836 /// Used for Data Binding between System.Web.UI. controls
839 /// System.Windows.Forms controls like a DataGrid
841 IList IListSource.GetList ()
843 IList list = (IList) _defaultView;
848 /// Copies a DataRow into a DataTable, preserving any
849 /// property settings, as well as original and current values.
852 public void ImportRow (DataRow row)
854 DataRow newRow = NewRow();
856 row.CopyValuesToRow(newRow);
861 /// This member is only meant to support Mono's infrastructure
863 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
869 dset = new DataSet ("tmpDataSet");
870 dset.Tables.Add (this);
873 StringWriter sw = new StringWriter ();
874 XmlTextWriter tw = new XmlTextWriter (sw);
875 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
878 StringWriter sw2 = new StringWriter ();
879 DataTableCollection tables = new DataTableCollection (dset);
881 XmlSchema schema = dset.BuildSchema (tables, null);
885 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
886 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
891 public void Load (IDataReader reader)
893 throw new NotImplementedException ();
897 public void Load (IDataReader reader, LoadOption loadOption)
899 throw new NotImplementedException ();
904 /// Finds and updates a specific row. If no matching row
905 /// is found, a new row is created using the given values.
908 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
911 if (PrimaryKey.Length == 0) {
912 row = Rows.Add (values);
914 row.AcceptChanges ();
917 bool hasPrimaryValues = true;
918 // initiate an array that has the values of the primary keys.
919 object[] keyValues = new object[PrimaryKey.Length];
920 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
922 if(PrimaryKey[i].Ordinal < values.Length)
923 keyValues[i] = values[PrimaryKey[i].Ordinal];
925 hasPrimaryValues = false;
928 if (hasPrimaryValues){
929 // find the row in the table.
930 row = Rows.Find(keyValues);
934 row = Rows.Add (values);
936 row.ItemArray = values;
939 row.AcceptChanges ();
947 public DataRow LoadDataRow (object[] values, LoadOption loadOption)
949 throw new NotImplementedException ();
953 public void Merge (DataTable table)
955 throw new NotImplementedException ();
959 public void Merge (DataTable table, bool preserveChanges)
961 throw new NotImplementedException ();
965 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
967 throw new NotImplementedException ();
972 /// Creates a new DataRow with the same schema as the table.
974 public DataRow NewRow ()
976 // initiate only one row builder.
977 if (_rowBuilder == null)
978 _rowBuilder = new DataRowBuilder (this, 0, 0);
980 // new row get id -1.
981 _rowBuilder._rowId = -1;
982 return this.NewRowFromBuilder (_rowBuilder);
986 /// This member supports the .NET Framework infrastructure
987 /// and is not intended to be used directly from your code.
989 protected internal DataRow[] NewRowArray (int size)
991 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
995 /// Creates a new row from an existing row.
997 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
999 return new DataRow (builder);
1006 XmlReadMode ReadXml (Stream stream)
1008 throw new NotImplementedException ();
1011 public void ReadXmlSchema (Stream stream)
1013 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1014 mapper.Read (new XmlTextReader(stream));
1017 public void ReadXmlSchema (TextReader reader)
1019 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1020 mapper.Read (new XmlTextReader(reader));
1023 public void ReadXmlSchema (string fileName)
1025 StreamReader reader = new StreamReader (fileName);
1026 ReadXmlSchema (reader);
1030 public void ReadXmlSchema (XmlReader reader)
1032 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1033 mapper.Read (reader);
1038 /// Rolls back all changes that have been made to the
1039 /// table since it was loaded, or the last time AcceptChanges
1043 public void RejectChanges ()
1045 for (int i = _rows.Count - 1; i >= 0; i--) {
1046 DataRow row = _rows [i];
1047 if (row.RowState != DataRowState.Unchanged)
1048 _rows [i].RejectChanges ();
1053 /// Resets the DataTable to its original state.
1056 public virtual void Reset ()
1059 while (ParentRelations.Count > 0)
1061 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
1062 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
1065 while (ChildRelations.Count > 0)
1067 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
1068 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
1070 Constraints.Clear();
1075 /// Gets an array of all DataRow objects.
1077 public DataRow[] Select ()
1079 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
1083 /// Gets an array of all DataRow objects that match
1084 /// the filter criteria in order of primary key (or
1085 /// lacking one, order of addition.)
1087 public DataRow[] Select (string filterExpression)
1089 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
1093 /// Gets an array of all DataRow objects that
1094 /// match the filter criteria, in the the
1095 /// specified sort order.
1097 public DataRow[] Select (string filterExpression, string sort)
1099 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
1103 /// Gets an array of all DataRow objects that match
1104 /// the filter in the order of the sort, that match
1105 /// the specified state.
1108 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
1110 DataRow[] dataRows = null;
1111 if (filterExpression == null)
1112 filterExpression = String.Empty;
1114 ExpressionElement Expression = null;
1115 if (filterExpression != null && filterExpression.Length != 0)
1116 Expression = new ExpressionMainElement(filterExpression);
1118 ArrayList List = new ArrayList();
1119 int recordStateFilter = GetRowStateFilter(recordStates);
1120 foreach (DataRow Row in Rows)
1122 if (((int)Row.RowState & recordStateFilter) != 0)
1124 if (Expression == null || Expression.Test(Row))
1129 dataRows = (DataRow[])List.ToArray(typeof(DataRow));
1132 if (sort != null && !sort.Equals(String.Empty))
1134 SortableColumn[] sortableColumns = null;
1136 sortableColumns = ParseTheSortString (sort);
1137 if (sortableColumns == null)
1138 throw new Exception ("sort expression result is null");
1139 if (sortableColumns.Length == 0)
1140 throw new Exception("sort expression result is 0");
1142 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
1143 dataRows = rowSorter.SortRows ();
1145 sortableColumns = null;
1153 internal void AddIndex (Index index)
1155 if (_indexes == null)
1156 _indexes = new ArrayList();
1158 _indexes.Add (index);
1161 internal void RemoveIndex (Index indx)
1163 _indexes.Remove (indx);
1166 internal Index GetIndexByColumns (DataColumn[] columns)
1168 return GetIndexByColumns(columns,false,false);
1171 // internal Index GetIndexByColumnsExtended(DataColumn[] columns)
1173 // DataColumn[] pkColumns = this.PrimaryKey;
1174 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1175 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1176 // Array.Copy(columns,0,cols,0,columns.Length);
1177 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1179 // return _getIndexByColumns(cols,false,false);
1185 internal Index GetIndexByColumns (DataColumn[] columns, bool unique)
1187 return GetIndexByColumns(columns,unique,true);
1190 // internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
1192 // DataColumn[] pkColumns = this.PrimaryKey;
1193 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1194 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1195 // Array.Copy(columns,0,cols,0,columns.Length);
1196 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1198 // return _getIndexByColumns(cols,unique,true);
1204 internal Index GetIndexByColumns(DataColumn[] columns, bool unique, bool useUnique)
1206 if (_indexes != null) {
1207 foreach (Index indx in _indexes) {
1209 if ((!useUnique) || ((useUnique)&& (indx.IsUnique))) {
1210 found = DataColumn.AreColumnSetsTheSame (indx.Columns, columns);
1220 internal void DeleteRowFromIndexes (DataRow row)
1222 if (_indexes != null) {
1223 foreach (Index indx in _indexes) {
1229 private static int GetRowStateFilter(DataViewRowState recordStates)
1233 if ((recordStates & DataViewRowState.Added) != 0)
1234 flag |= (int)DataRowState.Added;
1235 if ((recordStates & DataViewRowState.Deleted) != 0)
1236 flag |= (int)DataRowState.Deleted;
1237 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
1238 flag |= (int)DataRowState.Modified;
1239 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
1240 flag |= (int)DataRowState.Modified;
1241 if ((recordStates & DataViewRowState.Unchanged) != 0)
1242 flag |= (int)DataRowState.Unchanged;
1248 /// Gets the TableName and DisplayExpression, if
1249 /// there is one as a concatenated string.
1251 public override string ToString()
1253 //LAMESPEC: spec says concat the two. impl puts a
1254 //plus sign infront of DisplayExpression
1255 string retVal = TableName;
1256 if(DisplayExpression != null && DisplayExpression != "")
1257 retVal += " + " + DisplayExpression;
1263 public void WriteXml (Stream stream)
1265 throw new NotImplementedException ();
1269 public void WriteXml (TextWriter writer)
1271 throw new NotImplementedException ();
1275 public void WriteXml (XmlWriter writer)
1277 throw new NotImplementedException ();
1281 public void WriteXml (string fileName)
1283 throw new NotImplementedException ();
1287 public void WriteXml (Stream stream, XmlWriteMode mode)
1289 throw new NotImplementedException ();
1293 public void WriteXml (TextWriter writer, XmlWriteMode mode)
1295 throw new NotImplementedException ();
1299 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
1301 throw new NotImplementedException ();
1305 public void WriteXml (string fileName, XmlWriteMode mode)
1307 throw new NotImplementedException ();
1311 public void WriteXmlSchema (Stream stream)
1313 throw new NotImplementedException ();
1317 public void WriteXmlSchema (TextWriter writer)
1319 throw new NotImplementedException ();
1323 public void WriteXmlSchema (XmlWriter writer)
1325 throw new NotImplementedException ();
1329 public void WriteXmlSchema (string fileName)
1331 throw new NotImplementedException ();
1335 #region Events /////////////////
1338 /// Raises the ColumnChanged event.
1340 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1341 if (null != ColumnChanged) {
1342 ColumnChanged (this, e);
1347 /// Raises the ColumnChanging event.
1349 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1350 if (null != ColumnChanging) {
1351 ColumnChanging (this, e);
1356 /// Raises the PropertyChanging event.
1359 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1360 // if (null != PropertyChanging)
1362 // PropertyChanging (this, e);
1367 /// Notifies the DataTable that a DataColumn is being removed.
1370 protected internal virtual void OnRemoveColumn (DataColumn column) {
1371 // if (null != RemoveColumn)
1373 // RemoveColumn(this, e);
1378 /// Raises the RowChanged event.
1380 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1381 if (null != RowChanged) {
1382 RowChanged(this, e);
1388 /// Raises the RowChanging event.
1390 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1391 if (null != RowChanging) {
1392 RowChanging(this, e);
1397 /// Raises the RowDeleted event.
1399 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1400 if (null != RowDeleted) {
1401 RowDeleted(this, e);
1406 /// Raises the RowDeleting event.
1408 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1409 if (null != RowDeleting) {
1410 RowDeleting(this, e);
1415 private DataColumn CopyColumn (DataColumn Column) {
1416 DataColumn Copy = new DataColumn ();
1418 // Copy all the properties of column
1419 Copy.AllowDBNull = Column.AllowDBNull;
1420 Copy.AutoIncrement = Column.AutoIncrement;
1421 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1422 Copy.AutoIncrementStep = Column.AutoIncrementStep;
1423 Copy.Caption = Column.Caption;
1424 Copy.ColumnMapping = Column.ColumnMapping;
1425 Copy.ColumnName = Column.ColumnName;
1427 Copy.DataType = Column.DataType;
1428 Copy.DefaultValue = Column.DefaultValue;
1429 Copy.Expression = Column.Expression;
1430 //Copy.ExtendedProperties
1431 Copy.MaxLength = Column.MaxLength;
1432 Copy.Namespace = Column.Namespace;
1433 Copy.Prefix = Column.Prefix;
1434 Copy.ReadOnly = Column.ReadOnly;
1436 //we do not copy the unique value - it will be copyied when copying the constraints.
1437 //Copy.Unique = Column.Unique;
1443 /// Occurs when after a value has been changed for
1444 /// the specified DataColumn in a DataRow.
1446 [DataCategory ("Data")]
1447 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1448 public event DataColumnChangeEventHandler ColumnChanged;
1451 /// Occurs when a value is being changed for the specified
1452 /// DataColumn in a DataRow.
1454 [DataCategory ("Data")]
1455 [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.")]
1456 public event DataColumnChangeEventHandler ColumnChanging;
1459 /// Occurs after a DataRow has been changed successfully.
1461 [DataCategory ("Data")]
1462 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1463 public event DataRowChangeEventHandler RowChanged;
1466 /// Occurs when a DataRow is changing.
1468 [DataCategory ("Data")]
1469 [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.")]
1470 public event DataRowChangeEventHandler RowChanging;
1473 /// Occurs after a row in the table has been deleted.
1475 [DataCategory ("Data")]
1476 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
1477 public event DataRowChangeEventHandler RowDeleted;
1480 /// Occurs before a row in the table is about to be deleted.
1482 [DataCategory ("Data")]
1483 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1484 public event DataRowChangeEventHandler RowDeleting;
1486 #endregion // Events
1489 /// Removes all UniqueConstraints
1491 private void RemoveUniqueConstraints ()
1493 foreach (Constraint Cons in Constraints) {
1495 if (Cons is UniqueConstraint) {
1496 Constraints.Remove (Cons);
1501 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1504 // to parse the sort string for DataTable:Select(expression,sort)
1505 // into sortable columns (think ORDER BY,
1506 // such as, "customer ASC, price DESC" )
1507 private SortableColumn[] ParseTheSortString (string sort)
1509 SortableColumn[] sortColumns = null;
1510 ArrayList columns = null;
1512 if (sort != null && !sort.Equals ("")) {
1513 columns = new ArrayList ();
1514 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1516 for (int c = 0; c < columnExpression.Length; c++) {
1517 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1519 string columnName = columnSortInfo[0].Trim ();
1520 string sortOrder = "ASC";
1521 if (columnSortInfo.Length > 1)
1522 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
1524 ListSortDirection sortDirection = ListSortDirection.Ascending;
1525 switch (sortOrder) {
1527 sortDirection = ListSortDirection.Ascending;
1530 sortDirection = ListSortDirection.Descending;
1533 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1537 ord = Int32.Parse (columnName);
1539 catch (FormatException) {
1542 DataColumn dc = null;
1544 dc = _columnCollection[columnName];
1546 dc = _columnCollection[ord];
1547 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1548 columns.Add (sortCol);
1550 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1555 private class SortableColumn
1557 private DataColumn col;
1558 private ListSortDirection dir;
1560 internal SortableColumn (DataColumn column,
1561 ListSortDirection direction)
1567 public DataColumn Column {
1573 public ListSortDirection SortDirection {
1580 private class RowSorter : IComparer
1582 private SortableColumn[] sortColumns;
1583 private DataRow[] rowsToSort;
1585 internal RowSorter(DataRow[] unsortedRows,
1586 SortableColumn[] sortColumns)
1588 this.sortColumns = sortColumns;
1589 this.rowsToSort = unsortedRows;
1592 public SortableColumn[] SortColumns {
1598 public DataRow[] SortRows ()
1600 Array.Sort (rowsToSort, this);
1604 int IComparer.Compare (object x, object y)
1607 throw new Exception ("Object to compare is null: x");
1609 throw new Exception ("Object to compare is null: y");
1611 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1613 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1615 DataRow rowx = (DataRow) x;
1616 DataRow rowy = (DataRow) y;
1618 for(int i = 0; i < sortColumns.Length; i++) {
1619 SortableColumn sortColumn = sortColumns[i];
1620 DataColumn dc = sortColumn.Column;
1622 IComparable objx = (IComparable) rowx[dc];
1623 object objy = rowy[dc];
1625 int result = CompareObjects (objx, objy);
1627 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1638 private int CompareObjects (object a, object b)
1644 else if (a == DBNull.Value)
1648 else if (b == DBNull.Value)
1651 if((a is string) && (b is string)) {
1652 a = ((string) a).ToUpper ();
1653 b = ((string) b).ToUpper ();
1656 if (a is IComparable)
1657 return ((a as IComparable).CompareTo (b));
1658 else if (b is IComparable)
1659 return -((b as IComparable).CompareTo (a));
1661 throw new ArgumentException ("Neither a nor b IComparable");
1666 /// Creates new index for a table
1668 internal Index CreateIndex(string name, DataColumn[] columns, bool unique)
1670 // first check whenever index exists on the columns
1671 Index idx = this.GetIndexByColumns(columns);
1673 // if index on this columns already exists - return it
1678 Index newIndex = new Index(name,this,columns,unique);
1680 //InitializeIndex (newIndex);
1682 // add new index to table indexes
1683 this.AddIndex(newIndex);
1688 // /// Creates new extended index for a table
1690 // internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
1692 // // first check whenever extended index exists on the columns
1693 // Index idx = this.GetIndexByColumnsExtended(columns);
1694 // if(idx != null) {
1695 // // if extended index on this columns already exists - return it
1699 // DataColumn[] pkColumns = this.PrimaryKey;
1700 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1701 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1702 // Array.Copy(columns,0,cols,0,columns.Length);
1703 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1704 // return this.CreateIndex(name, cols, unique);
1707 // throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
1712 // /// Drops extended index if it is not referenced anymore
1713 // /// by any of table constraints
1715 // internal void DropIndexExtended(DataColumn[] columns)
1717 // // first check whenever extended index exists on the columns
1718 // Index index = this.GetIndexByColumnsExtended(columns);
1719 // if(index == null) {
1720 // // if no extended index on this columns exists - do nothing
1723 // this.DropIndex(index);
1727 /// Drops index specified by columns if it is not referenced anymore
1728 /// by any of table constraints
1730 internal void DropIndex(DataColumn[] columns)
1732 // first check whenever index exists for the columns
1733 Index index = this.GetIndexByColumns(columns);
1735 // if no index on this columns already exists - do nothing
1738 this.DropIndex(index);
1741 internal void DropIndex(Index index)
1743 // loop through table constraints and checks
1744 foreach(Constraint constraint in Constraints) {
1745 // if we found another reference to the index we do not remove the index.
1746 if (index == constraint.Index)
1750 this.RemoveIndex(index);
1753 internal void InitializeIndex (Index indx)
1755 DataRow[] rows = new DataRow[this.Rows.Count];
1756 this.Rows.CopyTo (rows, 0);
1758 // fill index with table rows
1759 foreach(DataRow row in this.Rows) {
1760 if(row.RowState != DataRowState.Deleted) {
1761 indx.Insert(new Node(row), DataRowVersion.Default);