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;
26 using Mono.Data.SqlExpressions;
28 namespace System.Data {
31 [DefaultEvent ("RowChanging")]
32 [DefaultProperty ("TableName")]
33 [DesignTimeVisible (false)]
35 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
37 internal DataSet dataSet;
39 private bool _caseSensitive;
40 private DataColumnCollection _columnCollection;
41 private ConstraintCollection _constraintCollection;
42 private DataView _defaultView;
44 private string _displayExpression;
45 private PropertyCollection _extendedProperties;
46 private bool _hasErrors;
47 private CultureInfo _locale;
48 private int _minimumCapacity;
49 private string _nameSpace;
50 private DataRelationCollection _childRelations;
51 private DataRelationCollection _parentRelations;
52 private string _prefix;
53 private DataColumn[] _primaryKey;
54 private DataRowCollection _rows;
56 private string _tableName;
57 private bool _containsListCollection;
58 private string _encodedTableName;
59 internal bool _duringDataLoad;
60 private bool dataSetPrevEnforceConstraints;
61 private DataRowBuilder _rowBuilder;
62 private ArrayList _indexes;
65 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
66 // CaseSensitive property. So when you lost you virginity it's gone for ever
67 private bool _virginCaseSensitive = true;
70 /// Initializes a new instance of the DataTable class with no arguments.
76 _columnCollection = new DataColumnCollection(this);
77 _constraintCollection = new ConstraintCollection(this);
78 _extendedProperties = new PropertyCollection();
81 _caseSensitive = false; //default value
82 _displayExpression = null;
85 _rows = new DataRowCollection (this);
86 _indexes = new ArrayList();
88 //LAMESPEC: spec says 25 impl does 50
89 _minimumCapacity = 50;
91 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
92 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
95 _defaultView = new DataView(this);
99 /// Intitalizes a new instance of the DataTable class with the specified table name.
101 public DataTable (string tableName) : this ()
103 _tableName = tableName;
107 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
110 protected DataTable (SerializationInfo info, StreamingContext context)
113 string schema = info.GetString ("XmlSchema");
114 string data = info.GetString ("XmlDiffGram");
116 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
117 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
120 XmlDiffLoader loader = new XmlDiffLoader (this);
121 xtr = new XmlTextReader(new StringReader (data));
126 public DataTable (string tableName, string tbNamespace)
129 _nameSpace = tbNamespace;
134 /// Indicates whether string comparisons within the table are case-sensitive.
136 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
137 public bool CaseSensitive {
138 get { return _caseSensitive; }
140 _virginCaseSensitive = false;
141 _caseSensitive = value;
145 internal bool VirginCaseSensitive {
146 get { return _virginCaseSensitive; }
147 set { _virginCaseSensitive = value; }
150 internal ArrayList Indexes{
151 get { return _indexes; }
154 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
156 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
160 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
162 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
163 OnColumnChanging (e);
166 internal void DeletedDataRow (DataRow dr, DataRowAction action)
168 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
172 internal void DeletingDataRow (DataRow dr, DataRowAction action)
174 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
178 internal void ChangedDataRow (DataRow dr, DataRowAction action)
180 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
184 internal void ChangingDataRow (DataRow dr, DataRowAction action)
186 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
191 /// Gets the collection of child relations for this DataTable.
194 [DataSysDescription ("Returns the child relations for this table.")]
195 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
196 public DataRelationCollection ChildRelations {
198 return _childRelations;
203 /// Gets the collection of columns that belong to this table.
205 [DataCategory ("Data")]
206 [DataSysDescription ("The collection that holds the columns for this table.")]
207 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
208 public DataColumnCollection Columns {
209 get { return _columnCollection; }
213 /// Gets the collection of constraints maintained by this table.
215 [DataCategory ("Data")]
216 [DataSysDescription ("The collection that holds the constraints for this table.")]
217 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
218 public ConstraintCollection Constraints {
219 get { return _constraintCollection; }
223 /// Gets the DataSet that this table belongs to.
226 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
227 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
228 public DataSet DataSet {
229 get { return dataSet; }
235 /// Gets a customized view of the table which may
236 /// include a filtered view, or a cursor position.
240 [DataSysDescription ("This is the default DataView for the table.")]
241 public DataView DefaultView {
242 get { return _defaultView; }
247 /// Gets or sets the expression that will return
248 /// a value used to represent this table in the user interface.
250 [DataCategory ("Data")]
251 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
253 public string DisplayExpression {
254 get { return "" + _displayExpression; }
255 set { _displayExpression = value; }
259 /// Gets the collection of customized user information.
262 [DataCategory ("Data")]
263 [DataSysDescription ("The collection that holds custom user information.")]
264 public PropertyCollection ExtendedProperties {
265 get { return _extendedProperties; }
269 /// Gets a value indicating whether there are errors in
270 /// any of the_rows in any of the tables of the DataSet to
271 /// which the table belongs.
274 [DataSysDescription ("Returns whether the table has errors.")]
275 public bool HasErrors {
277 // we can not use the _hasError flag because we do not know when to turn it off!
278 for (int i = 0; i < _rows.Count; i++)
280 if (_rows[i].HasErrors)
288 /// Gets or sets the locale information used to
289 /// compare strings within the table.
291 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
292 public CultureInfo Locale {
294 // if the locale is null, we check for the DataSet locale
295 // and if the DataSet is null we return the current culture.
296 // this way if DataSet locale is changed, only if there is no locale for
297 // the DataTable it influece the Locale get;
301 return DataSet.Locale;
302 return CultureInfo.CurrentCulture;
305 if (_locale == null || !_locale.Equals(value))
311 /// Gets or sets the initial starting size for this table.
313 [DataCategory ("Data")]
314 [DataSysDescription ("Indicates an initial starting size for this table.")]
316 public int MinimumCapacity {
317 get { return _minimumCapacity; }
318 set { _minimumCapacity = value; }
322 /// Gets or sets the namespace for the XML represenation
323 /// of the data stored in the DataTable.
325 [DataCategory ("Data")]
326 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
327 public string Namespace {
328 get { return "" + _nameSpace; }
329 set { _nameSpace = value; }
333 /// Gets the collection of parent relations for
337 [DataSysDescription ("Returns the parent relations for this table.")]
338 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
339 public DataRelationCollection ParentRelations {
341 return _parentRelations;
346 /// Gets or sets the namespace for the XML represenation
347 /// of the data stored in the DataTable.
349 [DataCategory ("Data")]
350 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
352 public string Prefix {
353 get { return "" + _prefix; }
354 set { _prefix = value; }
358 /// Gets or sets an array of columns that function as
359 /// primary keys for the data table.
361 [DataCategory ("Data")]
362 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
363 public DataColumn[] PrimaryKey {
365 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
366 if (null == uc) return new DataColumn[] {};
370 UniqueConstraint oldPKConstraint = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
372 // first check if value is the same as current PK.
373 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
376 // remove PK Constraint
377 if(oldPKConstraint != null) {
378 Constraints.Remove(oldPKConstraint);
382 //Does constraint exist for these columns
383 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
384 this.Constraints, (DataColumn[]) value);
386 //if constraint doesn't exist for columns
387 //create new unique primary key constraint
389 foreach (DataColumn Col in (DataColumn[]) value) {
391 if (Col.Table == null)
394 if (Columns.IndexOf (Col) < 0)
395 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
399 uc = new UniqueConstraint( (DataColumn[]) value, true);
401 Constraints.Add (uc);
404 //set existing constraint as the new primary key
405 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
408 // if we really supplied some columns fo primary key -
409 // rebuild indexes fo all foreign key constraints
410 if(value.Length > 0) {
411 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
412 constraint.AssertConstraint();
417 // if primary key is null now - drop all the indexes for foreign key constraints
418 foreach(ForeignKeyConstraint constraint in this.Constraints.ForeignKeyConstraints){
419 Index index = constraint.Index;
420 constraint.Index = null;
421 this.DropIndex(index);
429 /// Gets the collection of_rows that belong to this table.
432 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
433 public DataRowCollection Rows {
434 get { return _rows; }
438 /// Gets or sets an System.ComponentModel.ISite
439 /// for the DataTable.
442 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
443 public override ISite Site {
444 get { return _site; }
445 set { _site = value; }
449 /// Gets or sets the name of the the DataTable.
451 [DataCategory ("Data")]
452 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
454 [RefreshProperties (RefreshProperties.All)]
455 public string TableName {
456 get { return "" + _tableName; }
457 set { _tableName = value; }
460 bool IListSource.ContainsListCollection {
462 // the collection is a DataView
467 public bool RowsExist(Object[] columns, Object[] relatedColumns,DataRow row)
469 object[] vals = new object[relatedColumns.Length];
470 for (int i = 0; i < vals.Length; i++)
471 vals[i] = row[(DataColumn)relatedColumns[i]];
473 return RowsExist(columns,vals);
476 public bool RowsExist(Object[] columns,Object[] values)
478 bool rowsExist = false;
479 Index indx = this.GetIndexByColumns ((DataColumn[])columns);
481 if (indx != null) { // lookup for a row in index
482 rowsExist = (indx.FindSimple (values, false) != null);
483 } else { // no index we have to perform full-table scan
484 // check that there is a parent for this row.
485 foreach (DataRow thisRow in this.Rows) {
486 if (thisRow.RowState != DataRowState.Deleted) {
488 // check if the values in the columns are equal
489 for (int i = 0; i < columns.Length; i++) {
490 if (!thisRow[(DataColumn)columns[i]].Equals(values[i])) {
495 if (match) {// there is a row with columns values equals to those supplied.
506 /// Commits all the changes made to this table since the
507 /// last time AcceptChanges was called.
509 public void AcceptChanges ()
511 //FIXME: Do we need to validate anything here or
512 //try to catch any errors to deal with them?
514 // we do not use foreach because if one of the rows is in Delete state
515 // it will be romeved from Rows and we get an exception.
517 for (int i = 0; i < Rows.Count; )
520 myRow.AcceptChanges();
522 // if the row state is Detached it meens that it was removed from row list (Rows)
523 // so we should not increase 'i'.
524 if (myRow.RowState != DataRowState.Detached)
530 /// Begins the initialization of a DataTable that is used
531 /// on a form or used by another component. The initialization
532 /// occurs at runtime.
534 public virtual void BeginInit ()
539 /// Turns off notifications, index maintenance, and
540 /// constraints while loading data.
543 public void BeginLoadData ()
545 if (!this._duringDataLoad)
547 //duringDataLoad is important to EndLoadData and
548 //for not throwing unexpected exceptions.
549 this._duringDataLoad = true;
551 if (this.dataSet != null)
553 //Saving old Enforce constraints state for later
554 //use in the EndLoadData.
555 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
556 this.dataSet.EnforceConstraints = false;
563 /// Clears the DataTable of all data.
565 public void Clear () {
566 // TODO: thow an exception if any rows that
567 // have enforced child relations
568 // that would result in child rows being orphaned
569 // now we check if any ForeignKeyConstraint is referncing 'table'.
572 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
574 // loop on all tables in dataset
575 while (tableEnumerator.MoveNext())
577 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
578 // loop on all constrains in the current table
579 while (constraintEnumerator.MoveNext())
581 Object o = constraintEnumerator.Current;
582 // we only check ForeignKeyConstraint
583 if (o is ForeignKeyConstraint)
585 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
586 if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
587 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
593 // TODO: throw a NotSupportedException if the DataTable is part
594 // of a DataSet that is binded to an XmlDataDocument
600 /// Clones the structure of the DataTable, including
601 /// all DataTable schemas and constraints.
604 public virtual DataTable Clone ()
606 DataTable Copy = new DataTable ();
608 CopyProperties (Copy);
613 /// Computes the given expression on the current_rows that
614 /// pass the filter criteria.
617 public object Compute (string expression, string filter)
619 // expression is an aggregate function
620 // filter is an expression used to limit rows
622 DataRow[] rows = Select(filter);
624 Parser parser = new Parser (rows);
625 IExpression expr = parser.Compile (expression);
626 object obj = expr.Eval (rows[0]);
632 /// Copies both the structure and data for this DataTable.
635 public DataTable Copy ()
637 DataTable Copy = new DataTable ();
638 CopyProperties (Copy);
640 // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
641 // because if the row state is deleted we get an exception.
642 Copy._duringDataLoad = true;
643 foreach (DataRow Row in Rows) {
644 DataRow NewRow = Copy.NewRow ();
645 Copy.Rows.Add (NewRow);
646 Row.CopyValuesToRow(NewRow);
648 Copy._duringDataLoad = false;
653 private void CopyProperties (DataTable Copy)
656 Copy.CaseSensitive = CaseSensitive;
657 Copy.VirginCaseSensitive = VirginCaseSensitive;
659 // Copy.ChildRelations
664 Copy.DisplayExpression = DisplayExpression;
665 // Copy.ExtendedProperties
666 Copy.Locale = Locale;
667 Copy.MinimumCapacity = MinimumCapacity;
668 Copy.Namespace = Namespace;
669 // Copy.ParentRelations
670 Copy.Prefix = Prefix;
672 Copy.TableName = TableName;
677 foreach (DataColumn Column in Columns) {
679 Copy.Columns.Add (CopyColumn (Column));
682 CopyConstraints(Copy);
683 // add primary key to the copy
684 if (PrimaryKey.Length > 0)
686 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
687 for (int i = 0; i < pColumns.Length; i++)
688 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
690 Copy.PrimaryKey = pColumns;
694 private void CopyConstraints(DataTable copy)
696 UniqueConstraint origUc;
697 UniqueConstraint copyUc;
698 for (int i = 0; i < this.Constraints.Count; i++)
700 if (this.Constraints[i] is UniqueConstraint)
702 origUc = (UniqueConstraint)this.Constraints[i];
703 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
704 for (int j = 0; j < columns.Length; j++)
705 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
707 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
708 copy.Constraints.Add(copyUc);
713 /// Ends the initialization of a DataTable that is used
714 /// on a form or used by another component. The
715 /// initialization occurs at runtime.
718 public virtual void EndInit ()
724 /// Turns on notifications, index maintenance, and
725 /// constraints after loading data.
728 public void EndLoadData()
731 if (this._duringDataLoad)
733 //Returning from loading mode, raising exceptions as usual
734 this._duringDataLoad = false;
736 if (this.dataSet !=null)
738 //Getting back to previous EnforceConstraint state
739 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
741 for (i=0 ; i<this.Rows.Count ; i++)
743 if (this.Rows[i]._nullConstraintViolation )
745 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
755 /// Gets a copy of the DataTable that contains all
756 /// changes made to it since it was loaded or
757 /// AcceptChanges was last called.
760 public DataTable GetChanges()
762 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
766 /// Gets a copy of the DataTable containing all
767 /// changes made to it since it was last loaded, or
768 /// since AcceptChanges was called, filtered by DataRowState.
771 public DataTable GetChanges(DataRowState rowStates)
773 DataTable copyTable = null;
775 IEnumerator rowEnumerator = Rows.GetEnumerator();
776 while (rowEnumerator.MoveNext()) {
777 DataRow row = (DataRow)rowEnumerator.Current;
778 if (row.IsRowChanged(rowStates)) {
779 if (copyTable == null)
781 DataRow newRow = copyTable.NewRow();
782 copyTable.Rows.Add(newRow);
783 row.CopyValuesToRow(newRow);
792 public DataTableReader GetDataReader ()
794 throw new NotImplementedException ();
799 /// Gets an array of DataRow objects that contain errors.
802 public DataRow[] GetErrors ()
804 ArrayList errors = new ArrayList();
805 for (int i = 0; i < _rows.Count; i++)
807 if (_rows[i].HasErrors)
808 errors.Add(_rows[i]);
811 return (DataRow[]) errors.ToArray(typeof(DataRow));
815 /// This member is only meant to support Mono's infrastructure
817 protected virtual DataTable CreateInstance ()
819 return Activator.CreateInstance (this.GetType (), true) as DataTable;
823 /// This member is only meant to support Mono's infrastructure
825 protected virtual Type GetRowType ()
827 return typeof (DataRow);
831 /// This member is only meant to support Mono's infrastructure
833 /// Used for Data Binding between System.Web.UI. controls
836 /// System.Windows.Forms controls like a DataGrid
838 IList IListSource.GetList ()
840 IList list = (IList) _defaultView;
845 /// Copies a DataRow into a DataTable, preserving any
846 /// property settings, as well as original and current values.
849 public void ImportRow (DataRow row)
851 DataRow newRow = NewRow();
853 row.CopyValuesToRow(newRow);
858 /// This member is only meant to support Mono's infrastructure
860 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
866 dset = new DataSet ("tmpDataSet");
867 dset.Tables.Add (this);
870 StringWriter sw = new StringWriter ();
871 XmlTextWriter tw = new XmlTextWriter (sw);
872 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
875 StringWriter sw2 = new StringWriter ();
876 DataTableCollection tables = new DataTableCollection (dset);
878 XmlSchema schema = dset.BuildSchema (tables, null);
882 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
883 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
888 public void Load (IDataReader reader)
890 throw new NotImplementedException ();
894 public void Load (IDataReader reader, LoadOption loadOption)
896 throw new NotImplementedException ();
901 /// Finds and updates a specific row. If no matching row
902 /// is found, a new row is created using the given values.
905 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
908 if (PrimaryKey.Length == 0) {
909 row = Rows.Add (values);
911 row.AcceptChanges ();
914 bool hasPrimaryValues = true;
915 // initiate an array that has the values of the primary keys.
916 object[] keyValues = new object[PrimaryKey.Length];
917 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
919 if(PrimaryKey[i].Ordinal < values.Length)
920 keyValues[i] = values[PrimaryKey[i].Ordinal];
922 hasPrimaryValues = false;
925 if (hasPrimaryValues){
926 // find the row in the table.
927 row = Rows.Find(keyValues);
931 row = Rows.Add (values);
933 row.ItemArray = values;
936 row.AcceptChanges ();
944 public DataRow LoadDataRow (object[] values, LoadOption loadOption)
946 throw new NotImplementedException ();
950 public void Merge (DataTable table)
952 throw new NotImplementedException ();
956 public void Merge (DataTable table, bool preserveChanges)
958 throw new NotImplementedException ();
962 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
964 throw new NotImplementedException ();
969 /// Creates a new DataRow with the same schema as the table.
971 public DataRow NewRow ()
973 // initiate only one row builder.
974 if (_rowBuilder == null)
975 _rowBuilder = new DataRowBuilder (this, 0, 0);
977 // new row get id -1.
978 _rowBuilder._rowId = -1;
979 return this.NewRowFromBuilder (_rowBuilder);
983 /// This member supports the .NET Framework infrastructure
984 /// and is not intended to be used directly from your code.
986 protected internal DataRow[] NewRowArray (int size)
988 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
992 /// Creates a new row from an existing row.
994 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
996 return new DataRow (builder);
1003 XmlReadMode ReadXml (Stream stream)
1005 throw new NotImplementedException ();
1008 public void ReadXmlSchema (Stream stream)
1010 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1011 mapper.Read (new XmlTextReader(stream));
1014 public void ReadXmlSchema (TextReader reader)
1016 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1017 mapper.Read (new XmlTextReader(reader));
1020 public void ReadXmlSchema (string fileName)
1022 StreamReader reader = new StreamReader (fileName);
1023 ReadXmlSchema (reader);
1027 public void ReadXmlSchema (XmlReader reader)
1029 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
1030 mapper.Read (reader);
1035 /// Rolls back all changes that have been made to the
1036 /// table since it was loaded, or the last time AcceptChanges
1040 public void RejectChanges ()
1042 for (int i = _rows.Count - 1; i >= 0; i--) {
1043 DataRow row = _rows [i];
1044 if (row.RowState != DataRowState.Unchanged)
1045 _rows [i].RejectChanges ();
1050 /// Resets the DataTable to its original state.
1053 public virtual void Reset ()
1056 while (ParentRelations.Count > 0)
1058 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
1059 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
1062 while (ChildRelations.Count > 0)
1064 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
1065 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
1067 Constraints.Clear();
1072 /// Gets an array of all DataRow objects.
1074 public DataRow[] Select ()
1076 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
1080 /// Gets an array of all DataRow objects that match
1081 /// the filter criteria in order of primary key (or
1082 /// lacking one, order of addition.)
1084 public DataRow[] Select (string filterExpression)
1086 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
1090 /// Gets an array of all DataRow objects that
1091 /// match the filter criteria, in the the
1092 /// specified sort order.
1094 public DataRow[] Select (string filterExpression, string sort)
1096 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
1100 /// Gets an array of all DataRow objects that match
1101 /// the filter in the order of the sort, that match
1102 /// the specified state.
1105 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
1107 if (filterExpression == null)
1108 filterExpression = String.Empty;
1110 IExpression filter = null;
1111 if (filterExpression != String.Empty) {
1112 Parser parser = new Parser ();
1113 filter = parser.Compile (filterExpression);
1116 ArrayList rowList = new ArrayList();
1117 int recordStateFilter = GetRowStateFilter(recordStates);
1118 foreach (DataRow row in Rows)
1119 if (((int)row.RowState & recordStateFilter) != 0)
1120 if (filter != null && (bool)filter.Eval (row))
1123 DataRow[] dataRows = (DataRow[])rowList.ToArray(typeof(DataRow));
1125 if (sort != null && !sort.Equals(String.Empty))
1127 SortableColumn[] sortableColumns = null;
1129 sortableColumns = ParseTheSortString (sort);
1130 if (sortableColumns == null)
1131 throw new Exception ("sort expression result is null");
1132 if (sortableColumns.Length == 0)
1133 throw new Exception("sort expression result is 0");
1135 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
1136 dataRows = rowSorter.SortRows ();
1138 sortableColumns = null;
1146 internal void AddIndex (Index index)
1148 if (_indexes == null)
1149 _indexes = new ArrayList();
1151 _indexes.Add (index);
1154 internal void RemoveIndex (Index indx)
1156 _indexes.Remove (indx);
1159 internal Index GetIndexByColumns (DataColumn[] columns)
1161 return GetIndexByColumns(columns,false,false);
1164 // internal Index GetIndexByColumnsExtended(DataColumn[] columns)
1166 // DataColumn[] pkColumns = this.PrimaryKey;
1167 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1168 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1169 // Array.Copy(columns,0,cols,0,columns.Length);
1170 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1172 // return _getIndexByColumns(cols,false,false);
1178 internal Index GetIndexByColumns (DataColumn[] columns, bool unique)
1180 return GetIndexByColumns(columns,unique,true);
1183 // internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
1185 // DataColumn[] pkColumns = this.PrimaryKey;
1186 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1187 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1188 // Array.Copy(columns,0,cols,0,columns.Length);
1189 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1191 // return _getIndexByColumns(cols,unique,true);
1197 internal Index GetIndexByColumns(DataColumn[] columns, bool unique, bool useUnique)
1199 if (_indexes != null) {
1200 foreach (Index indx in _indexes) {
1202 if ((!useUnique) || ((useUnique)&& (indx.IsUnique))) {
1203 found = DataColumn.AreColumnSetsTheSame (indx.Columns, columns);
1213 internal void DeleteRowFromIndexes (DataRow row)
1215 if (_indexes != null) {
1216 foreach (Index indx in _indexes) {
1222 private static int GetRowStateFilter(DataViewRowState recordStates)
1226 if ((recordStates & DataViewRowState.Added) != 0)
1227 flag |= (int)DataRowState.Added;
1228 if ((recordStates & DataViewRowState.Deleted) != 0)
1229 flag |= (int)DataRowState.Deleted;
1230 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
1231 flag |= (int)DataRowState.Modified;
1232 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
1233 flag |= (int)DataRowState.Modified;
1234 if ((recordStates & DataViewRowState.Unchanged) != 0)
1235 flag |= (int)DataRowState.Unchanged;
1241 /// Gets the TableName and DisplayExpression, if
1242 /// there is one as a concatenated string.
1244 public override string ToString()
1246 //LAMESPEC: spec says concat the two. impl puts a
1247 //plus sign infront of DisplayExpression
1248 string retVal = TableName;
1249 if(DisplayExpression != null && DisplayExpression != "")
1250 retVal += " + " + DisplayExpression;
1256 public void WriteXml (Stream stream)
1258 throw new NotImplementedException ();
1262 public void WriteXml (TextWriter writer)
1264 throw new NotImplementedException ();
1268 public void WriteXml (XmlWriter writer)
1270 throw new NotImplementedException ();
1274 public void WriteXml (string fileName)
1276 throw new NotImplementedException ();
1280 public void WriteXml (Stream stream, XmlWriteMode mode)
1282 throw new NotImplementedException ();
1286 public void WriteXml (TextWriter writer, XmlWriteMode mode)
1288 throw new NotImplementedException ();
1292 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
1294 throw new NotImplementedException ();
1298 public void WriteXml (string fileName, XmlWriteMode mode)
1300 throw new NotImplementedException ();
1304 public void WriteXmlSchema (Stream stream)
1306 throw new NotImplementedException ();
1310 public void WriteXmlSchema (TextWriter writer)
1312 throw new NotImplementedException ();
1316 public void WriteXmlSchema (XmlWriter writer)
1318 throw new NotImplementedException ();
1322 public void WriteXmlSchema (string fileName)
1324 throw new NotImplementedException ();
1328 #region Events /////////////////
1331 /// Raises the ColumnChanged event.
1333 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1334 if (null != ColumnChanged) {
1335 ColumnChanged (this, e);
1340 /// Raises the ColumnChanging event.
1342 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1343 if (null != ColumnChanging) {
1344 ColumnChanging (this, e);
1349 /// Raises the PropertyChanging event.
1352 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1353 // if (null != PropertyChanging)
1355 // PropertyChanging (this, e);
1360 /// Notifies the DataTable that a DataColumn is being removed.
1363 protected internal virtual void OnRemoveColumn (DataColumn column) {
1364 // if (null != RemoveColumn)
1366 // RemoveColumn(this, e);
1371 /// Raises the RowChanged event.
1373 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1374 if (null != RowChanged) {
1375 RowChanged(this, e);
1381 /// Raises the RowChanging event.
1383 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1384 if (null != RowChanging) {
1385 RowChanging(this, e);
1390 /// Raises the RowDeleted event.
1392 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1393 if (null != RowDeleted) {
1394 RowDeleted(this, e);
1399 /// Raises the RowDeleting event.
1401 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1402 if (null != RowDeleting) {
1403 RowDeleting(this, e);
1408 private DataColumn CopyColumn (DataColumn Column) {
1409 DataColumn Copy = new DataColumn ();
1411 // Copy all the properties of column
1412 Copy.AllowDBNull = Column.AllowDBNull;
1413 Copy.AutoIncrement = Column.AutoIncrement;
1414 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1415 Copy.AutoIncrementStep = Column.AutoIncrementStep;
1416 Copy.Caption = Column.Caption;
1417 Copy.ColumnMapping = Column.ColumnMapping;
1418 Copy.ColumnName = Column.ColumnName;
1420 Copy.DataType = Column.DataType;
1421 Copy.DefaultValue = Column.DefaultValue;
1422 Copy.Expression = Column.Expression;
1423 //Copy.ExtendedProperties
1424 Copy.MaxLength = Column.MaxLength;
1425 Copy.Namespace = Column.Namespace;
1426 Copy.Prefix = Column.Prefix;
1427 Copy.ReadOnly = Column.ReadOnly;
1429 //we do not copy the unique value - it will be copyied when copying the constraints.
1430 //Copy.Unique = Column.Unique;
1436 /// Occurs when after a value has been changed for
1437 /// the specified DataColumn in a DataRow.
1439 [DataCategory ("Data")]
1440 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1441 public event DataColumnChangeEventHandler ColumnChanged;
1444 /// Occurs when a value is being changed for the specified
1445 /// DataColumn in a DataRow.
1447 [DataCategory ("Data")]
1448 [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.")]
1449 public event DataColumnChangeEventHandler ColumnChanging;
1452 /// Occurs after a DataRow has been changed successfully.
1454 [DataCategory ("Data")]
1455 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1456 public event DataRowChangeEventHandler RowChanged;
1459 /// Occurs when a DataRow is changing.
1461 [DataCategory ("Data")]
1462 [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.")]
1463 public event DataRowChangeEventHandler RowChanging;
1466 /// Occurs after a row in the table has been deleted.
1468 [DataCategory ("Data")]
1469 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
1470 public event DataRowChangeEventHandler RowDeleted;
1473 /// Occurs before a row in the table is about to be deleted.
1475 [DataCategory ("Data")]
1476 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1477 public event DataRowChangeEventHandler RowDeleting;
1479 #endregion // Events
1482 /// Removes all UniqueConstraints
1484 private void RemoveUniqueConstraints ()
1486 foreach (Constraint Cons in Constraints) {
1488 if (Cons is UniqueConstraint) {
1489 Constraints.Remove (Cons);
1494 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1497 // to parse the sort string for DataTable:Select(expression,sort)
1498 // into sortable columns (think ORDER BY,
1499 // such as, "customer ASC, price DESC" )
1500 private SortableColumn[] ParseTheSortString (string sort)
1502 SortableColumn[] sortColumns = null;
1503 ArrayList columns = null;
1505 if (sort != null && !sort.Equals ("")) {
1506 columns = new ArrayList ();
1507 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1509 for (int c = 0; c < columnExpression.Length; c++) {
1510 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1512 string columnName = columnSortInfo[0].Trim ();
1513 string sortOrder = "ASC";
1514 if (columnSortInfo.Length > 1)
1515 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
1517 ListSortDirection sortDirection = ListSortDirection.Ascending;
1518 switch (sortOrder) {
1520 sortDirection = ListSortDirection.Ascending;
1523 sortDirection = ListSortDirection.Descending;
1526 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1530 ord = Int32.Parse (columnName);
1532 catch (FormatException) {
1535 DataColumn dc = null;
1537 dc = _columnCollection[columnName];
1539 dc = _columnCollection[ord];
1540 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1541 columns.Add (sortCol);
1543 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1548 private class SortableColumn
1550 private DataColumn col;
1551 private ListSortDirection dir;
1553 internal SortableColumn (DataColumn column,
1554 ListSortDirection direction)
1560 public DataColumn Column {
1566 public ListSortDirection SortDirection {
1573 private class RowSorter : IComparer
1575 private SortableColumn[] sortColumns;
1576 private DataRow[] rowsToSort;
1578 internal RowSorter(DataRow[] unsortedRows,
1579 SortableColumn[] sortColumns)
1581 this.sortColumns = sortColumns;
1582 this.rowsToSort = unsortedRows;
1585 public SortableColumn[] SortColumns {
1591 public DataRow[] SortRows ()
1593 Array.Sort (rowsToSort, this);
1597 int IComparer.Compare (object x, object y)
1600 throw new Exception ("Object to compare is null: x");
1602 throw new Exception ("Object to compare is null: y");
1604 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1606 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1608 DataRow rowx = (DataRow) x;
1609 DataRow rowy = (DataRow) y;
1611 for(int i = 0; i < sortColumns.Length; i++) {
1612 SortableColumn sortColumn = sortColumns[i];
1613 DataColumn dc = sortColumn.Column;
1615 IComparable objx = (IComparable) rowx[dc];
1616 object objy = rowy[dc];
1618 int result = CompareObjects (objx, objy);
1620 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1631 private int CompareObjects (object a, object b)
1637 else if (a == DBNull.Value)
1641 else if (b == DBNull.Value)
1644 if((a is string) && (b is string)) {
1645 a = ((string) a).ToUpper ();
1646 b = ((string) b).ToUpper ();
1649 if (a is IComparable)
1650 return ((a as IComparable).CompareTo (b));
1651 else if (b is IComparable)
1652 return -((b as IComparable).CompareTo (a));
1654 throw new ArgumentException ("Neither a nor b IComparable");
1659 /// Creates new index for a table
1661 internal Index CreateIndex(string name, DataColumn[] columns, bool unique)
1663 // first check whenever index exists on the columns
1664 Index idx = this.GetIndexByColumns(columns);
1666 // if index on this columns already exists - return it
1671 Index newIndex = new Index(name,this,columns,unique);
1673 //InitializeIndex (newIndex);
1675 // add new index to table indexes
1676 this.AddIndex(newIndex);
1681 // /// Creates new extended index for a table
1683 // internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
1685 // // first check whenever extended index exists on the columns
1686 // Index idx = this.GetIndexByColumnsExtended(columns);
1687 // if(idx != null) {
1688 // // if extended index on this columns already exists - return it
1692 // DataColumn[] pkColumns = this.PrimaryKey;
1693 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1694 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1695 // Array.Copy(columns,0,cols,0,columns.Length);
1696 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1697 // return this.CreateIndex(name, cols, unique);
1700 // throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
1705 // /// Drops extended index if it is not referenced anymore
1706 // /// by any of table constraints
1708 // internal void DropIndexExtended(DataColumn[] columns)
1710 // // first check whenever extended index exists on the columns
1711 // Index index = this.GetIndexByColumnsExtended(columns);
1712 // if(index == null) {
1713 // // if no extended index on this columns exists - do nothing
1716 // this.DropIndex(index);
1720 /// Drops index specified by columns if it is not referenced anymore
1721 /// by any of table constraints
1723 internal void DropIndex(DataColumn[] columns)
1725 // first check whenever index exists for the columns
1726 Index index = this.GetIndexByColumns(columns);
1728 // if no index on this columns already exists - do nothing
1731 this.DropIndex(index);
1734 internal void DropIndex(Index index)
1736 // loop through table constraints and checks
1737 foreach(Constraint constraint in Constraints) {
1738 // if we found another reference to the index we do not remove the index.
1739 if (index == constraint.Index)
1743 this.RemoveIndex(index);
1746 internal void InitializeIndex (Index indx)
1748 DataRow[] rows = new DataRow[this.Rows.Count];
1749 this.Rows.CopyTo (rows, 0);
1751 // fill index with table rows
1752 foreach(DataRow row in this.Rows) {
1753 if(row.RowState != DataRowState.Deleted) {
1754 indx.Insert(new Node(row), DataRowVersion.Default);