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
11 // Sureshkumar T <tsureshkumar@novell.com>
\r
12 // Konstantin Triger <kostat@mainsoft.com>
\r
14 // (C) Chris Podurgiel
\r
15 // (C) Ximian, Inc 2002
\r
16 // Copyright (C) Tim Coleman, 2002-2003
\r
17 // Copyright (C) Daniel Morgan, 2002-2003
\r
21 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
\r
23 // Permission is hereby granted, free of charge, to any person obtaining
\r
24 // a copy of this software and associated documentation files (the
\r
25 // "Software"), to deal in the Software without restriction, including
\r
26 // without limitation the rights to use, copy, modify, merge, publish,
\r
27 // distribute, sublicense, and/or sell copies of the Software, and to
\r
28 // permit persons to whom the Software is furnished to do so, subject to
\r
29 // the following conditions:
\r
31 // The above copyright notice and this permission notice shall be
\r
32 // included in all copies or substantial portions of the Software.
\r
34 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
35 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
36 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
37 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
38 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
39 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
40 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
44 using System.Data.Common;
\r
45 using System.Collections;
\r
46 using System.ComponentModel;
\r
47 using System.Globalization;
\r
49 using System.Runtime.Serialization;
\r
51 using System.Xml.Schema;
\r
52 using System.Text.RegularExpressions;
\r
53 using Mono.Data.SqlExpressions;
\r
55 namespace System.Data {
\r
57 [ToolboxItem (false)]
\r
58 [DefaultEvent ("RowChanging")]
\r
59 [DefaultProperty ("TableName")]
\r
60 [DesignTimeVisible (false)]
\r
61 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
63 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
\r
67 internal DataSet dataSet;
\r
69 private bool _caseSensitive;
\r
70 private DataColumnCollection _columnCollection;
\r
71 private ConstraintCollection _constraintCollection;
\r
72 // never access it. Use DefaultView.
\r
73 private DataView _defaultView = null;
\r
75 private string _displayExpression;
\r
76 private PropertyCollection _extendedProperties;
\r
77 private bool _hasErrors;
\r
78 private CultureInfo _locale;
\r
79 private int _minimumCapacity;
\r
80 private string _nameSpace;
\r
81 private DataRelationCollection _childRelations;
\r
82 private DataRelationCollection _parentRelations;
\r
83 private string _prefix;
\r
84 private UniqueConstraint _primaryKeyConstraint;
\r
85 private DataRowCollection _rows;
\r
86 private ISite _site;
\r
87 private string _tableName;
\r
88 private bool _containsListCollection;
\r
89 private string _encodedTableName;
\r
90 internal bool _duringDataLoad;
\r
91 internal bool _nullConstraintViolationDuringDataLoad;
\r
92 private bool dataSetPrevEnforceConstraints;
\r
93 private bool dataTablePrevEnforceConstraints;
\r
94 private bool enforceConstraints = true;
\r
95 private DataRowBuilder _rowBuilder;
\r
96 private ArrayList _indexes;
\r
97 private RecordCache _recordCache;
\r
98 private int _defaultValuesRowIndex = -1;
\r
99 protected internal bool initInProgress;
\r
101 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
\r
102 // CaseSensitive property. So when you lost you virginity it's gone for ever
\r
103 private bool _virginCaseSensitive = true;
\r
105 private PropertyDescriptorCollection _propertyDescriptorsCache;
\r
106 static DataColumn[] _emptyColumnArray = new DataColumn[0];
\r
108 // Regex to parse the Sort string.
\r
109 static Regex SortRegex = new Regex ( @"^((\[(?<ColName>.+)\])|(?<ColName>\S+))([ ]+(?<Order>ASC|DESC))?$",
\r
110 RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture);
\r
113 DataColumn[] _latestPrimaryKeyCols;
\r
114 #endregion //Fields
\r
117 /// Initializes a new instance of the DataTable class with no arguments.
\r
119 public DataTable ()
\r
122 _columnCollection = new DataColumnCollection(this);
\r
123 _constraintCollection = new ConstraintCollection(this);
\r
124 _extendedProperties = new PropertyCollection();
\r
127 _caseSensitive = false; //default value
\r
128 _displayExpression = null;
\r
129 _primaryKeyConstraint = null;
\r
131 _rows = new DataRowCollection (this);
\r
132 _indexes = new ArrayList();
\r
133 _recordCache = new RecordCache(this);
\r
135 //LAMESPEC: spec says 25 impl does 50
\r
136 _minimumCapacity = 50;
\r
138 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
139 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
143 /// Intitalizes a new instance of the DataTable class with the specified table name.
\r
145 public DataTable (string tableName) : this ()
\r
147 _tableName = tableName;
\r
151 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
\r
154 protected DataTable (SerializationInfo info, StreamingContext context)
\r
157 string schema = info.GetString ("XmlSchema");
\r
158 string data = info.GetString ("XmlDiffGram");
\r
160 DataSet ds = new DataSet ();
\r
161 ds.ReadXmlSchema (new StringReader (schema));
\r
162 ds.Tables [0].CopyProperties (this);
\r
163 ds = new DataSet ();
\r
164 ds.Tables.Add (this);
\r
165 ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);
\r
166 ds.Tables.Remove (this);
\r
167 /* keeping for a while. With the change above, we shouldn't have to consider
\r
168 * DataTable mode in schema inference/read.
\r
169 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
\r
170 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
\r
173 XmlDiffLoader loader = new XmlDiffLoader (this);
\r
174 xtr = new XmlTextReader(new StringReader (data));
\r
180 public DataTable (string tableName, string tbNamespace)
\r
183 _nameSpace = tbNamespace;
\r
188 /// Indicates whether string comparisons within the table are case-sensitive.
\r
191 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
\r
193 public bool CaseSensitive {
\r
195 if (_virginCaseSensitive && dataSet != null)
\r
196 return dataSet.CaseSensitive;
\r
198 return _caseSensitive;
\r
201 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
202 throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
\r
204 _virginCaseSensitive = false;
\r
205 _caseSensitive = value;
\r
206 ResetCaseSensitiveIndexes();
\r
210 internal ArrayList Indexes{
\r
211 get { return _indexes; }
\r
214 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
\r
216 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
217 OnColumnChanged(e);
\r
220 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
\r
222 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
223 OnColumnChanging (e);
\r
226 internal void DeletedDataRow (DataRow dr, DataRowAction action)
\r
228 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
232 internal void DeletingDataRow (DataRow dr, DataRowAction action)
\r
234 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
238 internal void ChangedDataRow (DataRow dr, DataRowAction action)
\r
240 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
244 internal void ChangingDataRow (DataRow dr, DataRowAction action)
\r
246 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
251 /// Gets the collection of child relations for this DataTable.
\r
253 [Browsable (false)]
\r
255 [DataSysDescription ("Returns the child relations for this table.")]
\r
257 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
258 public DataRelationCollection ChildRelations {
\r
260 return _childRelations;
\r
265 /// Gets the collection of columns that belong to this table.
\r
267 [DataCategory ("Data")]
\r
269 [DataSysDescription ("The collection that holds the columns for this table.")]
\r
271 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
272 public DataColumnCollection Columns {
\r
273 get { return _columnCollection; }
\r
277 /// Gets the collection of constraints maintained by this table.
\r
279 [DataCategory ("Data")]
\r
281 [DataSysDescription ("The collection that holds the constraints for this table.")]
\r
283 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
284 public ConstraintCollection Constraints {
\r
285 get { return _constraintCollection; }
\r
289 /// Gets the DataSet that this table belongs to.
\r
291 [Browsable (false)]
\r
293 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
\r
295 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
296 public DataSet DataSet {
\r
297 get { return dataSet; }
\r
303 /// Gets a customized view of the table which may
\r
304 /// include a filtered view, or a cursor position.
\r
307 [Browsable (false)]
\r
309 [DataSysDescription ("This is the default DataView for the table.")]
\r
311 public DataView DefaultView {
\r
313 if (_defaultView == null) {
\r
315 if (_defaultView == null){
\r
316 if (dataSet != null)
\r
317 _defaultView = dataSet.DefaultViewManager.CreateDataView(this);
\r
319 _defaultView = new DataView(this);
\r
323 return _defaultView;
\r
329 /// Gets or sets the expression that will return
\r
330 /// a value used to represent this table in the user interface.
\r
332 [DataCategory ("Data")]
\r
334 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
\r
336 [DefaultValue ("")]
\r
337 public string DisplayExpression {
\r
338 get { return _displayExpression == null ? "" : _displayExpression; }
\r
339 set { _displayExpression = value; }
\r
343 /// Gets the collection of customized user information.
\r
345 [Browsable (false)]
\r
346 [DataCategory ("Data")]
\r
348 [DataSysDescription ("The collection that holds custom user information.")]
\r
350 public PropertyCollection ExtendedProperties {
\r
351 get { return _extendedProperties; }
\r
355 /// Gets a value indicating whether there are errors in
\r
356 /// any of the_rows in any of the tables of the DataSet to
\r
357 /// which the table belongs.
\r
359 [Browsable (false)]
\r
361 [DataSysDescription ("Returns whether the table has errors.")]
\r
363 public bool HasErrors {
\r
365 // we can not use the _hasError flag because we do not know when to turn it off!
\r
366 for (int i = 0; i < _rows.Count; i++)
\r
368 if (_rows[i].HasErrors)
\r
376 /// Gets or sets the locale information used to
\r
377 /// compare strings within the table.
\r
380 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
\r
382 public CultureInfo Locale {
\r
384 // if the locale is null, we check for the DataSet locale
\r
385 // and if the DataSet is null we return the current culture.
\r
386 // this way if DataSet locale is changed, only if there is no locale for
\r
387 // the DataTable it influece the Locale get;
\r
388 if (_locale != null)
\r
390 if (DataSet != null)
\r
391 return DataSet.Locale;
\r
392 return CultureInfo.CurrentCulture;
\r
395 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
396 throw new ArgumentException ("Cannot change CaseSensitive or Locale property. This change would lead to at least one DataRelation or Constraint to have different Locale or CaseSensitive settings between its related tables.");
\r
398 if (_locale == null || !_locale.Equals(value))
\r
404 /// Gets or sets the initial starting size for this table.
\r
406 [DataCategory ("Data")]
\r
408 [DataSysDescription ("Indicates an initial starting size for this table.")]
\r
410 [DefaultValue (50)]
\r
411 public int MinimumCapacity {
\r
412 get { return _minimumCapacity; }
\r
413 set { _minimumCapacity = value; }
\r
417 /// Gets or sets the namespace for the XML represenation
\r
418 /// of the data stored in the DataTable.
\r
420 [DataCategory ("Data")]
\r
422 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
\r
424 public string Namespace {
\r
427 if (_nameSpace != null)
\r
431 if (DataSet != null)
\r
433 return DataSet.Namespace;
\r
435 return String.Empty;
\r
437 set { _nameSpace = value; }
\r
441 /// Gets the collection of parent relations for
\r
442 /// this DataTable.
\r
444 [Browsable (false)]
\r
446 [DataSysDescription ("Returns the parent relations for this table.")]
\r
448 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
449 public DataRelationCollection ParentRelations {
\r
451 return _parentRelations;
\r
456 /// Gets or sets the namespace for the XML represenation
\r
457 /// of the data stored in the DataTable.
\r
459 [DataCategory ("Data")]
\r
461 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
\r
463 [DefaultValue ("")]
\r
464 public string Prefix {
\r
465 get { return _prefix == null ? "" : _prefix; }
\r
467 // Prefix cannot contain any special characters other than '_' and ':'
\r
468 for (int i = 0; i < value.Length; i++) {
\r
469 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
\r
470 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
\r
477 /// Gets or sets an array of columns that function as
\r
478 /// primary keys for the data table.
\r
480 [DataCategory ("Data")]
\r
482 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
\r
484 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
485 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, " + Consts.AssemblySystem_Data)]
\r
486 public DataColumn[] PrimaryKey {
\r
488 if (_primaryKeyConstraint == null) {
\r
489 return new DataColumn[] {};
\r
491 return _primaryKeyConstraint.Columns;
\r
494 if (value == null || value.Length == 0) {
\r
495 if (_primaryKeyConstraint != null) {
\r
496 _primaryKeyConstraint.SetIsPrimaryKey (false);
\r
497 Constraints.Remove(_primaryKeyConstraint);
\r
498 _primaryKeyConstraint = null;
\r
503 if (InitInProgress) {
\r
504 _latestPrimaryKeyCols = value;
\r
508 // first check if value is the same as current PK.
\r
509 if (_primaryKeyConstraint!= null && DataColumn.AreColumnSetsTheSame(value, _primaryKeyConstraint.Columns))
\r
512 //Does constraint exist for these columns
\r
513 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(this.Constraints, (DataColumn[]) value);
\r
515 //if constraint doesn't exist for columns
\r
516 //create new unique primary key constraint
\r
518 foreach (DataColumn Col in (DataColumn[]) value) {
\r
519 if (Col.Table == null)
\r
522 if (Columns.IndexOf (Col) < 0)
\r
523 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
\r
525 // create constraint with primary key indication set to false
\r
526 // to avoid recursion
\r
527 uc = new UniqueConstraint( (DataColumn[]) value, false);
\r
528 Constraints.Add (uc);
\r
531 //Remove the existing primary key
\r
532 if (_primaryKeyConstraint != null) {
\r
533 _primaryKeyConstraint.SetIsPrimaryKey (false);
\r
534 Constraints.Remove (_primaryKeyConstraint);
\r
535 _primaryKeyConstraint = null;
\r
538 //set the constraint as the new primary key
\r
539 UniqueConstraint.SetAsPrimaryKey (Constraints, uc);
\r
540 _primaryKeyConstraint = uc;
\r
542 for (int j=0; j < uc.Columns.Length; ++j)
\r
543 uc.Columns[j].AllowDBNull = false;
\r
547 internal UniqueConstraint PrimaryKeyConstraint {
\r
549 return _primaryKeyConstraint;
\r
554 /// Gets the collection of_rows that belong to this table.
\r
556 [Browsable (false)]
\r
558 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
\r
560 public DataRowCollection Rows {
\r
561 get { return _rows; }
\r
565 /// Gets or sets an System.ComponentModel.ISite
\r
566 /// for the DataTable.
\r
568 [Browsable (false)]
\r
569 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
570 public override ISite Site {
\r
571 get { return _site; }
\r
572 set { _site = value; }
\r
576 /// Gets or sets the name of the the DataTable.
\r
578 [DataCategory ("Data")]
\r
580 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
\r
582 [DefaultValue ("")]
\r
583 [RefreshProperties (RefreshProperties.All)]
\r
584 public string TableName {
\r
585 get { return _tableName == null ? "" : _tableName; }
\r
586 set { _tableName = value; }
\r
589 bool IListSource.ContainsListCollection {
\r
591 // the collection is a DataView
\r
596 internal RecordCache RecordCache {
\r
598 return _recordCache;
\r
602 private DataRowBuilder RowBuilder
\r
606 // initiate only one row builder.
\r
607 if (_rowBuilder == null)
\r
608 _rowBuilder = new DataRowBuilder (this, -1, 0);
\r
610 // new row get id -1.
\r
611 _rowBuilder._rowId = -1;
\r
613 return _rowBuilder;
\r
617 internal bool EnforceConstraints {
\r
618 get { return enforceConstraints; }
\r
620 if (value != enforceConstraints) {
\r
622 // reset indexes since they may be outdated
\r
625 bool violatesConstraints = false;
\r
627 //FIXME: use index for AllowDBNull
\r
628 for (int i = 0; i < Columns.Count; i++) {
\r
629 DataColumn column = Columns[i];
\r
630 if (!column.AllowDBNull) {
\r
631 for (int j = 0; j < Rows.Count; j++){
\r
632 if (Rows[j].IsNull(column)) {
\r
633 violatesConstraints = true;
\r
634 Rows[j].RowError = String.Format("Column '{0}' does not allow DBNull.Value.", column.ColumnName);
\r
640 if (violatesConstraints)
\r
641 Constraint.ThrowConstraintException();
\r
643 // assert all constraints
\r
644 foreach (Constraint constraint in Constraints) {
\r
645 constraint.AssertConstraint();
\r
648 enforceConstraints = value;
\r
653 internal bool RowsExist(DataColumn[] columns, DataColumn[] relatedColumns,DataRow row)
\r
655 int curIndex = row.IndexFromVersion(DataRowVersion.Default);
\r
656 int tmpRecord = RecordCache.NewRecord();
\r
659 for (int i = 0; i < relatedColumns.Length; i++) {
\r
660 // according to MSDN: the DataType value for both columns must be identical.
\r
661 columns[i].DataContainer.CopyValue(relatedColumns[i].DataContainer, curIndex, tmpRecord);
\r
663 return RowsExist(columns, tmpRecord);
\r
666 RecordCache.DisposeRecord(tmpRecord);
\r
670 bool RowsExist(DataColumn[] columns, int index)
\r
672 bool rowsExist = false;
\r
673 Index indx = this.FindIndex(columns);
\r
675 if (indx != null) { // lookup for a row in index
\r
676 rowsExist = (indx.Find(index) != -1);
\r
679 // we have to perform full-table scan
\r
680 // check that there is a parent for this row.
\r
681 foreach (DataRow thisRow in this.Rows) {
\r
682 if (thisRow.RowState != DataRowState.Deleted) {
\r
684 // check if the values in the columns are equal
\r
685 int thisIndex = thisRow.IndexFromVersion(DataRowVersion.Current);
\r
686 foreach (DataColumn column in columns) {
\r
687 if (column.DataContainer.CompareValues(thisIndex, index) != 0) {
\r
692 if (match) {// there is a row with columns values equals to those supplied.
\r
703 /// Commits all the changes made to this table since the
\r
704 /// last time AcceptChanges was called.
\r
706 public void AcceptChanges ()
\r
708 //FIXME: Do we need to validate anything here or
\r
709 //try to catch any errors to deal with them?
\r
711 // we do not use foreach because if one of the rows is in Delete state
\r
712 // it will be romeved from Rows and we get an exception.
\r
714 for (int i = 0; i < Rows.Count; )
\r
717 myRow.AcceptChanges();
\r
719 // if the row state is Detached it meens that it was removed from row list (Rows)
\r
720 // so we should not increase 'i'.
\r
721 if (myRow.RowState != DataRowState.Detached)
\r
727 /// Begins the initialization of a DataTable that is used
\r
728 /// on a form or used by another component. The initialization
\r
729 /// occurs at runtime.
\r
731 public virtual void BeginInit ()
\r
733 InitInProgress = true;
\r
737 /// Turns off notifications, index maintenance, and
\r
738 /// constraints while loading data.
\r
741 public void BeginLoadData ()
\r
743 if (!this._duringDataLoad)
\r
745 //duringDataLoad is important to EndLoadData and
\r
746 //for not throwing unexpected exceptions.
\r
747 this._duringDataLoad = true;
\r
748 this._nullConstraintViolationDuringDataLoad = false;
\r
750 if (this.dataSet != null)
\r
752 //Saving old Enforce constraints state for later
\r
753 //use in the EndLoadData.
\r
754 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
\r
755 this.dataSet.EnforceConstraints = false;
\r
758 //if table does not belong to any data set use EnforceConstraints of the table
\r
759 this.EnforceConstraints = false;
\r
766 /// Clears the DataTable of all data.
\r
768 public void Clear () {
\r
769 // Foriegn key constraints are checked in _rows.Clear method
\r
771 foreach(Index index in Indexes)
\r
774 OnTableCleared (new DataTableClearEventArgs (this));
\r
780 /// Clones the structure of the DataTable, including
\r
781 /// all DataTable schemas and constraints.
\r
783 public virtual DataTable Clone ()
\r
785 // Use Activator so we can use non-public constructors.
\r
786 DataTable Copy = (DataTable) Activator.CreateInstance(GetType(), true);
\r
787 CopyProperties (Copy);
\r
792 /// Computes the given expression on the current_rows that
\r
793 /// pass the filter criteria.
\r
796 public object Compute (string expression, string filter)
\r
798 // expression is an aggregate function
\r
799 // filter is an expression used to limit rows
\r
801 DataRow[] rows = Select(filter);
\r
803 if (rows == null || rows.Length == 0)
\r
804 return DBNull.Value;
\r
806 Parser parser = new Parser (rows);
\r
807 IExpression expr = parser.Compile (expression);
\r
808 object obj = expr.Eval (rows[0]);
\r
814 /// Copies both the structure and data for this DataTable.
\r
816 public DataTable Copy ()
\r
818 DataTable copy = Clone();
\r
820 copy._duringDataLoad = true;
\r
821 foreach (DataRow row in Rows) {
\r
822 DataRow newRow = copy.NewNotInitializedRow();
\r
823 copy.Rows.AddInternal(newRow);
\r
824 CopyRow(row,newRow);
\r
826 copy._duringDataLoad = false;
\r
828 // rebuild copy indexes after loading all rows
\r
829 copy.ResetIndexes();
\r
833 internal void CopyRow(DataRow fromRow,DataRow toRow)
\r
835 if (fromRow.HasErrors) {
\r
836 fromRow.CopyErrors(toRow);
\r
839 if (fromRow.HasVersion(DataRowVersion.Original)) {
\r
840 toRow.Original = toRow.Table.RecordCache.CopyRecord(this,fromRow.Original,-1);
\r
843 if (fromRow.HasVersion(DataRowVersion.Current)) {
\r
844 if (fromRow.Original != fromRow.Current) {
\r
845 toRow.Current = toRow.Table.RecordCache.CopyRecord(this,fromRow.Current,-1);
\r
848 toRow.Current = toRow.Original;
\r
853 private void CopyProperties (DataTable Copy)
\r
855 Copy.CaseSensitive = CaseSensitive;
\r
856 Copy._virginCaseSensitive = _virginCaseSensitive;
\r
858 // Copy.ChildRelations
\r
859 // Copy.Constraints
\r
861 // Copy.DefaultView
\r
863 Copy.DisplayExpression = DisplayExpression;
\r
864 if(ExtendedProperties.Count > 0) {
\r
865 // Cannot copy extended properties directly as the property does not have a set accessor
\r
866 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
\r
867 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
\r
868 for (int i=0; i < ExtendedProperties.Count; i++)
\r
869 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
\r
871 Copy.Locale = Locale;
\r
872 Copy.MinimumCapacity = MinimumCapacity;
\r
873 Copy.Namespace = Namespace;
\r
874 // Copy.ParentRelations
\r
875 Copy.Prefix = Prefix;
\r
877 Copy.TableName = TableName;
\r
879 bool isEmpty = Copy.Columns.Count == 0;
\r
882 foreach (DataColumn column in Columns) {
\r
883 // When cloning a table, the columns may be added in the default constructor.
\r
884 if (isEmpty || !Copy.Columns.Contains(column.ColumnName)) {
\r
885 Copy.Columns.Add (column.Clone());
\r
889 CopyConstraints(Copy);
\r
890 // add primary key to the copy
\r
891 if (PrimaryKey.Length > 0) {
\r
892 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
\r
893 for (int i = 0; i < pColumns.Length; i++)
\r
894 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
\r
896 Copy.PrimaryKey = pColumns;
\r
900 private void CopyConstraints(DataTable copy)
\r
902 UniqueConstraint origUc;
\r
903 UniqueConstraint copyUc;
\r
904 for (int i = 0; i < this.Constraints.Count; i++)
\r
906 if (this.Constraints[i] is UniqueConstraint)
\r
908 // typed ds can already contain the constraints
\r
909 if (copy.Constraints.Contains (this.Constraints [i].ConstraintName))
\r
912 origUc = (UniqueConstraint)this.Constraints[i];
\r
913 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
\r
914 for (int j = 0; j < columns.Length; j++)
\r
915 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
\r
917 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
\r
918 copy.Constraints.Add(copyUc);
\r
923 /// Ends the initialization of a DataTable that is used
\r
924 /// on a form or used by another component. The
\r
925 /// initialization occurs at runtime.
\r
928 public virtual void EndInit ()
\r
930 InitInProgress = false;
\r
934 internal bool InitInProgress {
\r
935 get { return initInProgress; }
\r
936 set { initInProgress = value; }
\r
939 internal void FinishInit ()
\r
941 UniqueConstraint oldPK = _primaryKeyConstraint;
\r
943 // Columns shud be added 'before' the constraints
\r
944 Columns.PostAddRange ();
\r
946 // Add the constraints
\r
947 _constraintCollection.PostAddRange ();
\r
949 // ms.net behavior : If a PrimaryKey (UniqueConstraint) is added thru AddRange,
\r
950 // then it takes precedence over an direct assignment of PrimaryKey
\r
951 if (_primaryKeyConstraint == oldPK)
\r
952 PrimaryKey = _latestPrimaryKeyCols;
\r
956 /// Turns on notifications, index maintenance, and
\r
957 /// constraints after loading data.
\r
959 public void EndLoadData()
\r
961 if (this._duringDataLoad) {
\r
962 if(this._nullConstraintViolationDuringDataLoad) {
\r
963 this._nullConstraintViolationDuringDataLoad = false;
\r
964 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
\r
967 if (this.dataSet !=null) {
\r
968 //Getting back to previous EnforceConstraint state
\r
969 this.dataSet.InternalEnforceConstraints(this.dataSetPrevEnforceConstraints,true);
\r
972 //Getting back to the table's previous EnforceConstraint state
\r
973 this.EnforceConstraints = true;
\r
976 //Returning from loading mode, raising exceptions as usual
\r
977 this._duringDataLoad = false;
\r
982 /// Gets a copy of the DataTable that contains all
\r
983 /// changes made to it since it was loaded or
\r
984 /// AcceptChanges was last called.
\r
986 public DataTable GetChanges()
\r
988 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
\r
992 /// Gets a copy of the DataTable containing all
\r
993 /// changes made to it since it was last loaded, or
\r
994 /// since AcceptChanges was called, filtered by DataRowState.
\r
996 public DataTable GetChanges(DataRowState rowStates)
\r
998 DataTable copyTable = null;
\r
1000 IEnumerator rowEnumerator = Rows.GetEnumerator();
\r
1001 while (rowEnumerator.MoveNext()) {
\r
1002 DataRow row = (DataRow)rowEnumerator.Current;
\r
1003 // The spec says relationship constraints may cause Unchanged parent rows to be included but
\r
1004 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
\r
1005 if (row.IsRowChanged(rowStates)) {
\r
1006 if (copyTable == null)
\r
1007 copyTable = Clone();
\r
1008 DataRow newRow = copyTable.NewNotInitializedRow();
\r
1009 row.CopyValuesToRow(newRow);
\r
1010 copyTable.Rows.AddInternal (newRow);
\r
1019 public DataTableReader GetDataReader ()
\r
1021 throw new NotImplementedException ();
\r
1026 /// Gets an array of DataRow objects that contain errors.
\r
1028 public DataRow[] GetErrors ()
\r
1030 ArrayList errors = new ArrayList();
\r
1031 for (int i = 0; i < _rows.Count; i++)
\r
1033 if (_rows[i].HasErrors)
\r
1034 errors.Add(_rows[i]);
\r
1037 DataRow[] ret = NewRowArray(errors.Count);
\r
1038 errors.CopyTo(ret, 0);
\r
1043 /// This member is only meant to support Mono's infrastructure
\r
1045 protected virtual DataTable CreateInstance ()
\r
1047 return Activator.CreateInstance (this.GetType (), true) as DataTable;
\r
1051 /// This member is only meant to support Mono's infrastructure
\r
1053 protected virtual Type GetRowType ()
\r
1055 return typeof (DataRow);
\r
1059 /// This member is only meant to support Mono's infrastructure
\r
1061 /// Used for Data Binding between System.Web.UI. controls
\r
1062 /// like a DataGrid
\r
1064 /// System.Windows.Forms controls like a DataGrid
\r
1066 IList IListSource.GetList ()
\r
1068 IList list = (IList) DefaultView;
\r
1073 /// Copies a DataRow into a DataTable, preserving any
\r
1074 /// property settings, as well as original and current values.
\r
1076 public void ImportRow (DataRow row)
\r
1078 if (row.RowState == DataRowState.Detached)
\r
1081 DataRow newRow = NewNotInitializedRow();
\r
1083 int original = -1;
\r
1084 if (row.HasVersion(DataRowVersion.Original)) {
\r
1085 original = row.IndexFromVersion(DataRowVersion.Original);
\r
1086 newRow.Original = RecordCache.NewRecord();
\r
1087 RecordCache.CopyRecord(row.Table,original,newRow.Original);
\r
1090 if (row.HasVersion(DataRowVersion.Current)) {
\r
1091 int current = row.IndexFromVersion(DataRowVersion.Current);
\r
1092 if (current == original)
\r
1093 newRow.Current = newRow.Original;
\r
1095 newRow.Current = RecordCache.NewRecord();
\r
1096 RecordCache.CopyRecord(row.Table,current,newRow.Current);
\r
1100 //Import the row only if RowState is not detached
\r
1101 //Validation for Deleted Rows happens during Accept/RejectChanges
\r
1102 if (row.RowState != DataRowState.Deleted)
\r
1103 newRow.Validate();
\r
1105 AddRowToIndexes (newRow);
\r
1106 Rows.AddInternal(newRow);
\r
1108 if (row.HasErrors) {
\r
1109 row.CopyErrors(newRow);
\r
1113 internal int DefaultValuesRowIndex
\r
1116 return _defaultValuesRowIndex;
\r
1121 /// This member is only meant to support Mono's infrastructure
\r
1123 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
\r
1126 if (dataSet != null)
\r
1129 dset = new DataSet ("tmpDataSet");
\r
1130 dset.Tables.Add (this);
\r
1133 StringWriter sw = new StringWriter ();
\r
1134 XmlTextWriter tw = new XmlTextWriter (sw);
\r
1135 tw.Formatting = Formatting.Indented;
\r
1136 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
\r
1139 StringWriter sw2 = new StringWriter ();
\r
1140 DataTableCollection tables = new DataTableCollection (dset);
\r
1141 tables.Add (this);
\r
1142 XmlSchemaWriter.WriteXmlSchema (dset, new XmlTextWriter (sw2), tables, null);
\r
1145 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
\r
1146 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
\r
1151 /// Loads the table with the values from the reader
\r
1153 public void Load (IDataReader reader)
\r
1155 Load (reader, LoadOption.PreserveChanges);
\r
1159 /// Loads the table with the values from the reader and the pattern
\r
1160 /// of the changes to the existing rows or the new rows are based on
\r
1161 /// the LoadOption passed.
\r
1163 public void Load (IDataReader reader, LoadOption loadOption)
\r
1165 bool prevEnforceConstr = this.EnforceConstraints;
\r
1167 this.EnforceConstraints = false;
\r
1168 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,
\r
1169 MissingSchemaAction.AddWithKey,
\r
1170 MissingMappingAction.Passthrough,
\r
1171 new DataTableMappingCollection ());
\r
1172 DbDataAdapter.FillFromReader (this,
\r
1179 this.EnforceConstraints = prevEnforceConstr;
\r
1187 /// Finds and updates a specific row. If no matching row
\r
1188 /// is found, a new row is created using the given values.
\r
1190 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
\r
1192 DataRow row = null;
\r
1193 if (PrimaryKey.Length == 0) {
\r
1194 row = Rows.Add (values);
\r
1195 if (fAcceptChanges)
\r
1196 row.AcceptChanges ();
\r
1199 EnsureDefaultValueRowIndex();
\r
1200 int newRecord = CreateRecord(values);
\r
1201 int existingRecord = _primaryKeyConstraint.Index.Find(newRecord);
\r
1203 if (existingRecord < 0) {
\r
1204 row = NewRowFromBuilder (RowBuilder);
\r
1205 row.Proposed = newRecord;
\r
1206 AddRowToIndexes (row);
\r
1207 Rows.AddInternal(row);
\r
1210 row = RecordCache[existingRecord];
\r
1212 row.ImportRecord(newRecord);
\r
1216 if (fAcceptChanges)
\r
1217 row.AcceptChanges ();
\r
1223 internal DataRow LoadDataRow(IDataRecord record, int[] mapping, int length, bool fAcceptChanges)
\r
1225 DataRow row = null;
\r
1226 int tmpRecord = this.RecordCache.NewRecord();
\r
1228 RecordCache.ReadIDataRecord(tmpRecord,record,mapping,length);
\r
1229 if (PrimaryKey.Length != 0) {
\r
1230 bool hasPrimaryValues = true;
\r
1231 foreach(DataColumn col in PrimaryKey) {
\r
1232 if(!(col.Ordinal < mapping.Length)) {
\r
1233 hasPrimaryValues = false;
\r
1238 if (hasPrimaryValues) {
\r
1239 // find the row in the table.
\r
1240 row = Rows.Find(tmpRecord);
\r
1244 bool shouldUpdateIndex = false;
\r
1245 if (row == null) {
\r
1246 row = NewNotInitializedRow();
\r
1247 row.ImportRecord(tmpRecord);
\r
1248 Rows.AddInternal (row);
\r
1249 shouldUpdateIndex = true;
\r
1252 // Proposed = tmpRecord
\r
1253 row.ImportRecord(tmpRecord);
\r
1256 if (fAcceptChanges) {
\r
1257 row.AcceptChanges();
\r
1260 if (shouldUpdateIndex && !fAcceptChanges) {
\r
1261 AddRowToIndexes(row);
\r
1265 catch(Exception e) {
\r
1266 this.RecordCache.DisposeRecord(tmpRecord);
\r
1274 /// Loads the given values into an existing row if matches or creates
\r
1275 /// the new row popluated with the values.
\r
1278 /// This method searches for the values using primary keys and it first
\r
1279 /// searches using the original values of the rows and if not found, it
\r
1280 /// searches using the current version of the row.
\r
1282 public DataRow LoadDataRow (object [] values, LoadOption loadOption)
\r
1284 DataRow row = null;
\r
1286 // Find Data DataRow
\r
1287 if (this.PrimaryKey.Length > 0) {
\r
1288 object [] keys = new object [PrimaryKey.Length];
\r
1289 for (int i=0; i < PrimaryKey.Length; i++)
\r
1290 keys [i] = values [PrimaryKey [i].Ordinal];
\r
1292 row = Rows.Find(keys, DataViewRowState.OriginalRows);
\r
1294 row = Rows.Find (keys);
\r
1297 // If not found, add new row
\r
1299 || (row.RowState == DataRowState.Deleted
\r
1300 && loadOption == LoadOption.Upsert)) {
\r
1301 row = NewNotInitializedRow ();
\r
1302 row.ImportRecord (CreateRecord(values));
\r
1304 row.Validate(); // this adds to index ;-)
\r
1306 if (loadOption == LoadOption.OverwriteChanges ||
\r
1307 loadOption == LoadOption.PreserveChanges) {
\r
1308 Rows.AddInternal(row);
\r
1309 ChangingDataRow (row, DataRowAction.ChangeCurrentAndOriginal);
\r
1310 row.Original = row.Current;
\r
1311 ChangedDataRow (row, DataRowAction.ChangeCurrentAndOriginal);
\r
1313 Rows.AddInternal(row);
\r
1317 if (row.RowState == DataRowState.Deleted
\r
1318 && loadOption == LoadOption.OverwriteChanges)
\r
1319 row.RejectChanges ();
\r
1321 row.Load (values, loadOption);
\r
1327 public void Merge (DataTable table)
\r
1329 throw new NotImplementedException ();
\r
1333 public void Merge (DataTable table, bool preserveChanges)
\r
1335 throw new NotImplementedException ();
\r
1339 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
\r
1341 throw new NotImplementedException ();
\r
1346 /// Creates a new DataRow with the same schema as the table.
\r
1348 public DataRow NewRow ()
\r
1350 EnsureDefaultValueRowIndex();
\r
1352 DataRow newRow = NewRowFromBuilder (RowBuilder);
\r
1354 newRow.Proposed = CreateRecord(null);
\r
1358 internal int CreateRecord(object[] values) {
\r
1359 int valCount = values != null ? values.Length : 0;
\r
1360 if (valCount > Columns.Count)
\r
1361 throw new ArgumentException("Input array is longer than the number of columns in this table.");
\r
1363 int index = RecordCache.NewRecord();
\r
1367 for (int i = 0; i < valCount; i++) {
\r
1368 object value = values[i];
\r
1369 if (value == null)
\r
1370 Columns[i].SetDefaultValue(index);
\r
1372 Columns[i][index] = values[i];
\r
1375 for(int i = valCount; i < Columns.Count; i++) {
\r
1376 Columns[i].SetDefaultValue(index);
\r
1382 RecordCache.DisposeRecord(index);
\r
1387 private void EnsureDefaultValueRowIndex()
\r
1389 // initialize default values row for the first time
\r
1390 if ( _defaultValuesRowIndex == -1 ) {
\r
1391 _defaultValuesRowIndex = RecordCache.NewRecord();
\r
1392 foreach(DataColumn column in Columns) {
\r
1393 column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;
\r
1399 internal int CompareRecords(int x, int y) {
\r
1400 for (int col = 0; col < Columns.Count; col++) {
\r
1401 int res = Columns[col].DataContainer.CompareValues (x, y);
\r
1411 /// This member supports the .NET Framework infrastructure
\r
1412 /// and is not intended to be used directly from your code.
\r
1414 protected internal DataRow[] NewRowArray (int size)
\r
1416 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
\r
1420 /// Creates a new row from an existing row.
\r
1422 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
1424 return new DataRow (builder);
\r
1427 internal DataRow NewNotInitializedRow()
\r
1429 EnsureDefaultValueRowIndex();
\r
1431 return NewRowFromBuilder (RowBuilder);
\r
1436 XmlReadMode ReadXml (Stream stream)
\r
1438 throw new NotImplementedException ();
\r
1441 public void ReadXmlSchema (Stream stream)
\r
1443 ReadXmlSchema (new XmlTextReader (stream));
\r
1446 public void ReadXmlSchema (TextReader reader)
\r
1448 ReadXmlSchema (new XmlTextReader (reader));
\r
1451 public void ReadXmlSchema (string fileName)
\r
1453 XmlTextReader reader = null;
\r
1455 reader = new XmlTextReader (fileName);
\r
1456 ReadXmlSchema (reader);
\r
1458 if (reader != null)
\r
1463 public void ReadXmlSchema (XmlReader reader)
\r
1465 DataSet ds = new DataSet ();
\r
1466 new XmlSchemaDataImporter (ds, reader).Process ();
\r
1467 DataTable target = null;
\r
1468 if (TableName == String.Empty) {
\r
1469 if (ds.Tables.Count > 0)
\r
1470 target = ds.Tables [0];
\r
1473 target = ds.Tables [TableName];
\r
1474 if (target == null)
\r
1475 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));
\r
1477 if (target != null)
\r
1478 target.CopyProperties (this);
\r
1483 /// Rolls back all changes that have been made to the
\r
1484 /// table since it was loaded, or the last time AcceptChanges
\r
1487 public void RejectChanges ()
\r
1489 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
1490 DataRow row = _rows [i];
\r
1491 if (row.RowState != DataRowState.Unchanged)
\r
1492 _rows [i].RejectChanges ();
\r
1497 /// Resets the DataTable to its original state.
\r
1499 public virtual void Reset ()
\r
1502 while (ParentRelations.Count > 0)
\r
1504 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
\r
1505 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
\r
1508 while (ChildRelations.Count > 0)
\r
1510 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
\r
1511 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
\r
1513 Constraints.Clear();
\r
1518 /// Gets an array of all DataRow objects.
\r
1520 public DataRow[] Select ()
\r
1522 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
1526 /// Gets an array of all DataRow objects that match
\r
1527 /// the filter criteria in order of primary key (or
\r
1528 /// lacking one, order of addition.)
\r
1530 public DataRow[] Select (string filterExpression)
\r
1532 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
1536 /// Gets an array of all DataRow objects that
\r
1537 /// match the filter criteria, in the the
\r
1538 /// specified sort order.
\r
1540 public DataRow[] Select (string filterExpression, string sort)
\r
1542 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
\r
1546 /// Gets an array of all DataRow objects that match
\r
1547 /// the filter in the order of the sort, that match
\r
1548 /// the specified state.
\r
1551 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
\r
1553 if (filterExpression == null)
\r
1554 filterExpression = String.Empty;
\r
1556 DataColumn[] columns = _emptyColumnArray;
\r
1557 ListSortDirection[] sorts = null;
\r
1558 if (sort != null && !sort.Equals(String.Empty))
\r
1559 columns = ParseSortString (this, sort, out sorts, false);
\r
1561 IExpression filter = null;
\r
1562 if (filterExpression != String.Empty) {
\r
1563 Parser parser = new Parser ();
\r
1564 filter = parser.Compile (filterExpression);
\r
1567 Index index = GetIndex(columns, sorts, recordStates, filter, false);
\r
1569 int[] records = index.GetAll();
\r
1570 DataRow[] dataRows = NewRowArray(index.Size);
\r
1571 for (int i = 0; i < dataRows.Length; i++)
\r
1572 dataRows[i] = RecordCache[records[i]];
\r
1578 private void AddIndex (Index index)
\r
1580 if (_indexes == null) {
\r
1581 _indexes = new ArrayList();
\r
1584 _indexes.Add (index);
\r
1588 /// Returns index corresponding to columns,sort,row state filter and unique values given.
\r
1589 /// If such an index not exists, creates a new one.
\r
1591 /// <param name="columns">Columns set of the index to look for.</param>
\r
1592 /// <param name="sort">Columns sort order of the index to look for.</param>
\r
1593 /// <param name="rowState">Rpw state filter of the index to look for.</param>
\r
1594 /// <param name="unique">Uniqueness of the index to look for.</param>
\r
1595 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>
\r
1596 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>
\r
1597 /// <returns></returns>
\r
1598 internal Index GetIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)
\r
1600 Index index = FindIndex(columns,sort,rowState,filter);
\r
1601 if (index == null ) {
\r
1602 index = new Index(new Key(this,columns,sort,rowState,filter));
\r
1607 // reset existing index only if asked for this
\r
1613 internal Index FindIndex(DataColumn[] columns)
\r
1615 return FindIndex(columns,null,DataViewRowState.None, null);
\r
1618 internal Index FindIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)
\r
1620 if (Indexes != null) {
\r
1621 foreach (Index index in Indexes) {
\r
1622 if (index.Key.Equals(columns,sort,rowState, filter)) {
\r
1630 internal void ResetIndexes()
\r
1632 foreach(Index index in Indexes) {
\r
1637 internal void ResetCaseSensitiveIndexes()
\r
1639 foreach(Index index in Indexes) {
\r
1640 bool containsStringcolumns = false;
\r
1641 foreach(DataColumn column in index.Key.Columns) {
\r
1642 if (column.DataType == typeof(string)) {
\r
1643 containsStringcolumns = true;
\r
1648 if (!containsStringcolumns && index.Key.HasFilter)
\r
1649 foreach (DataColumn column in Columns)
\r
1650 if ((column.DataType == DbTypes.TypeOfString) && (index.Key.DependsOn (column))) {
\r
1651 containsStringcolumns = true;
\r
1655 if (containsStringcolumns) {
\r
1661 internal void DropIndex(Index index)
\r
1663 if (index != null && index.RefCount == 0) {
\r
1664 _indexes.Remove(index);
\r
1668 internal void DropReferencedIndexes (DataColumn column)
\r
1670 if (_indexes != null)
\r
1671 for (int i = _indexes.Count - 1; i >= 0; i--) {
\r
1672 Index indx = (Index)_indexes [i];
\r
1673 if (indx.Key.DependsOn (column))
\r
1674 _indexes.Remove (indx);
\r
1678 internal void AddRowToIndexes (DataRow row) {
\r
1679 if (_indexes != null) {
\r
1680 foreach (Index indx in _indexes) {
\r
1686 internal void DeleteRowFromIndexes (DataRow row)
\r
1688 if (_indexes != null) {
\r
1689 foreach (Index indx in _indexes) {
\r
1690 indx.Delete (row);
\r
1696 /// Gets the TableName and DisplayExpression, if
\r
1697 /// there is one as a concatenated string.
\r
1699 public override string ToString()
\r
1701 //LAMESPEC: spec says concat the two. impl puts a
\r
1702 //plus sign infront of DisplayExpression
\r
1703 string retVal = TableName;
\r
1704 if(DisplayExpression != null && DisplayExpression != "")
\r
1705 retVal += " + " + DisplayExpression;
\r
1710 private XmlWriterSettings GetWriterSettings ()
\r
1712 XmlWriterSettings s = new XmlWriterSettings ();
\r
1717 public void WriteXml (Stream stream)
\r
1719 WriteXml (stream, XmlWriteMode.IgnoreSchema);
\r
1722 public void WriteXml (TextWriter writer)
\r
1724 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1727 public void WriteXml (XmlWriter writer)
\r
1729 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1732 public void WriteXml (string fileName)
\r
1734 WriteXml (fileName, XmlWriteMode.IgnoreSchema);
\r
1737 public void WriteXml (Stream stream, XmlWriteMode mode)
\r
1739 WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode);
\r
1742 public void WriteXml (TextWriter writer, XmlWriteMode mode)
\r
1744 WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode);
\r
1748 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
\r
1750 throw new NotImplementedException ();
\r
1753 public void WriteXml (string fileName, XmlWriteMode mode)
\r
1755 XmlWriter xw = null;
\r
1757 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1758 WriteXml (xw, mode);
\r
1765 public void WriteXmlSchema (Stream stream)
\r
1767 WriteXmlSchema (XmlWriter.Create (stream, GetWriterSettings ()));
\r
1770 public void WriteXmlSchema (TextWriter writer)
\r
1772 WriteXmlSchema (XmlWriter.Create (writer, GetWriterSettings ()));
\r
1775 public void WriteXmlSchema (XmlWriter writer)
\r
1777 DataSet ds = DataSet;
\r
1778 DataSet tmp = null;
\r
1781 tmp = ds = new DataSet ();
\r
1782 ds.Tables.Add (this);
\r
1784 DataTableCollection col = new DataTableCollection (ds);
\r
1786 XmlSchemaWriter.WriteXmlSchema (ds, writer, col, null);
\r
1789 ds.Tables.Remove (this);
\r
1793 public void WriteXmlSchema (string fileName)
\r
1795 XmlWriter xw = null;
\r
1797 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1798 WriteXmlSchema (xw);
\r
1809 /// Raises the ColumnChanged event.
\r
1811 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
\r
1812 if (null != ColumnChanged) {
\r
1813 ColumnChanged (this, e);
\r
1817 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {
\r
1818 OnColumnChanged(e);
\r
1823 /// Raises TableCleared Event and delegates to subscribers
\r
1825 protected virtual void OnTableCleared (DataTableClearEventArgs e) {
\r
1826 if (TableCleared != null)
\r
1827 TableCleared (this, e);
\r
1832 /// Raises the ColumnChanging event.
\r
1834 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
\r
1835 if (null != ColumnChanging) {
\r
1836 ColumnChanging (this, e);
\r
1840 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e) {
\r
1841 OnColumnChanging(e);
\r
1845 /// Raises the PropertyChanging event.
\r
1848 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
\r
1849 // if (null != PropertyChanging)
\r
1851 // PropertyChanging (this, e);
\r
1856 /// Notifies the DataTable that a DataColumn is being removed.
\r
1858 protected internal virtual void OnRemoveColumn (DataColumn column) {
\r
1859 DropReferencedIndexes (column);
\r
1864 /// Raises the RowChanged event.
\r
1866 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
\r
1867 if (null != RowChanged) {
\r
1868 RowChanged(this, e);
\r
1874 /// Raises the RowChanging event.
\r
1876 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
\r
1877 if (null != RowChanging) {
\r
1878 RowChanging(this, e);
\r
1883 /// Raises the RowDeleted event.
\r
1885 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
\r
1886 if (null != RowDeleted) {
\r
1887 RowDeleted(this, e);
\r
1892 /// Raises the RowDeleting event.
\r
1894 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
\r
1895 if (null != RowDeleting) {
\r
1896 RowDeleting(this, e);
\r
1901 /// Occurs when after a value has been changed for
\r
1902 /// the specified DataColumn in a DataRow.
\r
1904 [DataCategory ("Data")]
\r
1906 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1908 public event DataColumnChangeEventHandler ColumnChanged;
\r
1911 /// Occurs when a value is being changed for the specified
\r
1912 /// DataColumn in a DataRow.
\r
1914 [DataCategory ("Data")]
\r
1916 [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
1918 public event DataColumnChangeEventHandler ColumnChanging;
\r
1921 /// Occurs after a DataRow has been changed successfully.
\r
1923 [DataCategory ("Data")]
\r
1925 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1927 public event DataRowChangeEventHandler RowChanged;
\r
1930 /// Occurs when a DataRow is changing.
\r
1932 [DataCategory ("Data")]
\r
1934 [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
1936 public event DataRowChangeEventHandler RowChanging;
\r
1939 /// Occurs after a row in the table has been deleted.
\r
1941 [DataCategory ("Data")]
\r
1943 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1945 public event DataRowChangeEventHandler RowDeleted;
\r
1948 /// Occurs before a row in the table is about to be deleted.
\r
1950 [DataCategory ("Data")]
\r
1952 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1954 public event DataRowChangeEventHandler RowDeleting;
\r
1958 /// Occurs after the Clear method is called on the datatable.
\r
1960 [DataCategory ("Data")]
\r
1962 [DataSysDescription ("Occurs when the rows in a table is cleared . Throw an exception to cancel the deletion.")]
\r
1964 public event DataTableClearEventHandler TableCleared;
\r
1967 #endregion // Events
\r
1969 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)
\r
1971 DataColumn[] sortColumns = _emptyColumnArray;
\r
1972 sortDirections = null;
\r
1974 ArrayList columns = null;
\r
1975 ArrayList sorts = null;
\r
1977 if (sort != null && !sort.Equals ("")) {
\r
1978 columns = new ArrayList ();
\r
1979 sorts = new ArrayList();
\r
1980 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1982 for (int c = 0; c < columnExpression.Length; c++) {
\r
1983 string rawColumnName = columnExpression[c].Trim ();
\r
1985 Match match = SortRegex.Match (rawColumnName);
\r
1986 CaptureCollection cc = match.Groups["ColName"].Captures ;
\r
1987 if (cc.Count == 0)
\r
1988 throw new IndexOutOfRangeException ("Could not find column: " + rawColumnName);
\r
1990 string columnName = cc[0].Value;
\r
1991 DataColumn dc = table.Columns[columnName];
\r
1994 dc = table.Columns[Int32.Parse (columnName)];
\r
1995 } catch (FormatException) {
\r
1996 throw new IndexOutOfRangeException("Cannot find column " + columnName);
\r
2001 cc = match.Groups["Order"].Captures;
\r
2002 if (cc.Count == 0 || String.Compare (cc[0].Value, "ASC", true) == 0)
\r
2003 sorts.Add(ListSortDirection.Ascending);
\r
2005 sorts.Add (ListSortDirection.Descending);
\r
2008 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));
\r
2009 sortDirections = new ListSortDirection[sorts.Count];
\r
2010 for (int i = 0; i < sortDirections.Length; i++)
\r
2011 sortDirections[i] = (ListSortDirection)sorts[i];
\r
2014 if (rejectNoResult) {
\r
2015 if (sortColumns == null)
\r
2016 throw new SystemException ("sort expression result is null");
\r
2017 if (sortColumns.Length == 0)
\r
2018 throw new SystemException("sort expression result is 0");
\r
2021 return sortColumns;
\r
2024 private void UpdatePropertyDescriptorsCache()
\r
2026 PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];
\r
2028 foreach(DataColumn col in Columns) {
\r
2029 descriptors[index++] = new DataColumnPropertyDescriptor(col);
\r
2032 foreach(DataRelation rel in ChildRelations) {
\r
2033 descriptors[index++] = new DataRelationPropertyDescriptor(rel);
\r
2036 _propertyDescriptorsCache = new PropertyDescriptorCollection(descriptors);
\r
2039 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()
\r
2041 if (_propertyDescriptorsCache == null) {
\r
2042 UpdatePropertyDescriptorsCache();
\r
2045 return _propertyDescriptorsCache;
\r
2048 internal void ResetPropertyDescriptorsCache() {
\r
2049 _propertyDescriptorsCache = null;
\r