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 Mono.Data.SqlExpressions;
\r
54 namespace System.Data {
\r
56 [ToolboxItem (false)]
\r
57 [DefaultEvent ("RowChanging")]
\r
58 [DefaultProperty ("TableName")]
\r
59 [DesignTimeVisible (false)]
\r
60 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
62 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
\r
66 internal DataSet dataSet;
\r
68 private bool _caseSensitive;
\r
69 private DataColumnCollection _columnCollection;
\r
70 private ConstraintCollection _constraintCollection;
\r
71 // never access it. Use DefaultView.
\r
72 private DataView _defaultView = null;
\r
74 private string _displayExpression;
\r
75 private PropertyCollection _extendedProperties;
\r
76 private bool _hasErrors;
\r
77 private CultureInfo _locale;
\r
78 private int _minimumCapacity;
\r
79 private string _nameSpace;
\r
80 private DataRelationCollection _childRelations;
\r
81 private DataRelationCollection _parentRelations;
\r
82 private string _prefix;
\r
83 private UniqueConstraint _primaryKeyConstraint;
\r
84 private DataRowCollection _rows;
\r
85 private ISite _site;
\r
86 private string _tableName;
\r
87 private bool _containsListCollection;
\r
88 private string _encodedTableName;
\r
89 internal bool _duringDataLoad;
\r
90 internal bool _nullConstraintViolationDuringDataLoad;
\r
91 private bool dataSetPrevEnforceConstraints;
\r
92 private bool dataTablePrevEnforceConstraints;
\r
93 private bool enforceConstraints = true;
\r
94 private DataRowBuilder _rowBuilder;
\r
95 private ArrayList _indexes;
\r
96 private RecordCache _recordCache;
\r
97 private int _defaultValuesRowIndex = -1;
\r
98 protected internal bool fInitInProgress;
\r
100 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
\r
101 // CaseSensitive property. So when you lost you virginity it's gone for ever
\r
102 private bool _virginCaseSensitive = true;
\r
104 private PropertyDescriptorCollection _propertyDescriptorsCache;
\r
105 static DataColumn[] _emptyColumnArray = new DataColumn[0];
\r
107 #endregion //Fields
\r
110 /// Initializes a new instance of the DataTable class with no arguments.
\r
112 public DataTable ()
\r
115 _columnCollection = new DataColumnCollection(this);
\r
116 _constraintCollection = new ConstraintCollection(this);
\r
117 _extendedProperties = new PropertyCollection();
\r
120 _caseSensitive = false; //default value
\r
121 _displayExpression = null;
\r
122 _primaryKeyConstraint = null;
\r
124 _rows = new DataRowCollection (this);
\r
125 _indexes = new ArrayList();
\r
126 _recordCache = new RecordCache(this);
\r
128 //LAMESPEC: spec says 25 impl does 50
\r
129 _minimumCapacity = 50;
\r
131 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
132 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
136 /// Intitalizes a new instance of the DataTable class with the specified table name.
\r
138 public DataTable (string tableName) : this ()
\r
140 _tableName = tableName;
\r
144 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
\r
147 protected DataTable (SerializationInfo info, StreamingContext context)
\r
150 string schema = info.GetString ("XmlSchema");
\r
151 string data = info.GetString ("XmlDiffGram");
\r
153 DataSet ds = new DataSet ();
\r
154 ds.ReadXmlSchema (new StringReader (schema));
\r
155 ds.Tables [0].CopyProperties (this);
\r
156 ds = new DataSet ();
\r
157 ds.Tables.Add (this);
\r
158 ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);
\r
159 ds.Tables.Remove (this);
\r
160 /* keeping for a while. With the change above, we shouldn't have to consider
\r
161 * DataTable mode in schema inference/read.
\r
162 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
\r
163 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
\r
166 XmlDiffLoader loader = new XmlDiffLoader (this);
\r
167 xtr = new XmlTextReader(new StringReader (data));
\r
173 public DataTable (string tableName, string tbNamespace)
\r
176 _nameSpace = tbNamespace;
\r
181 /// Indicates whether string comparisons within the table are case-sensitive.
\r
183 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
\r
184 public bool CaseSensitive {
\r
186 if (_virginCaseSensitive && dataSet != null)
\r
187 return dataSet.CaseSensitive;
\r
189 return _caseSensitive;
\r
192 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
193 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
195 _virginCaseSensitive = false;
\r
196 _caseSensitive = value;
\r
197 ResetCaseSensitiveIndexes();
\r
201 internal ArrayList Indexes{
\r
202 get { return _indexes; }
\r
205 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
\r
207 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
208 OnColumnChanged(e);
\r
211 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
\r
213 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
214 OnColumnChanging (e);
\r
217 internal void DeletedDataRow (DataRow dr, DataRowAction action)
\r
219 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
223 internal void DeletingDataRow (DataRow dr, DataRowAction action)
\r
225 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
229 internal void ChangedDataRow (DataRow dr, DataRowAction action)
\r
231 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
235 internal void ChangingDataRow (DataRow dr, DataRowAction action)
\r
237 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
242 /// Gets the collection of child relations for this DataTable.
\r
244 [Browsable (false)]
\r
245 [DataSysDescription ("Returns the child relations for this table.")]
\r
246 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
247 public DataRelationCollection ChildRelations {
\r
249 return _childRelations;
\r
254 /// Gets the collection of columns that belong to this table.
\r
256 [DataCategory ("Data")]
\r
257 [DataSysDescription ("The collection that holds the columns for this table.")]
\r
258 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
259 public DataColumnCollection Columns {
\r
260 get { return _columnCollection; }
\r
264 /// Gets the collection of constraints maintained by this table.
\r
266 [DataCategory ("Data")]
\r
267 [DataSysDescription ("The collection that holds the constraints for this table.")]
\r
268 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
269 public ConstraintCollection Constraints {
\r
270 get { return _constraintCollection; }
\r
274 /// Gets the DataSet that this table belongs to.
\r
276 [Browsable (false)]
\r
277 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
\r
278 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
279 public DataSet DataSet {
\r
280 get { return dataSet; }
\r
286 /// Gets a customized view of the table which may
\r
287 /// include a filtered view, or a cursor position.
\r
290 [Browsable (false)]
\r
291 [DataSysDescription ("This is the default DataView for the table.")]
\r
292 public DataView DefaultView {
\r
294 if (_defaultView == null) {
\r
296 if (_defaultView == null){
\r
297 if (dataSet != null)
\r
298 _defaultView = dataSet.DefaultViewManager.CreateDataView(this);
\r
300 _defaultView = new DataView(this);
\r
304 return _defaultView;
\r
310 /// Gets or sets the expression that will return
\r
311 /// a value used to represent this table in the user interface.
\r
313 [DataCategory ("Data")]
\r
314 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
\r
315 [DefaultValue ("")]
\r
316 public string DisplayExpression {
\r
317 get { return _displayExpression == null ? "" : _displayExpression; }
\r
318 set { _displayExpression = value; }
\r
322 /// Gets the collection of customized user information.
\r
324 [Browsable (false)]
\r
325 [DataCategory ("Data")]
\r
326 [DataSysDescription ("The collection that holds custom user information.")]
\r
327 public PropertyCollection ExtendedProperties {
\r
328 get { return _extendedProperties; }
\r
332 /// Gets a value indicating whether there are errors in
\r
333 /// any of the_rows in any of the tables of the DataSet to
\r
334 /// which the table belongs.
\r
336 [Browsable (false)]
\r
337 [DataSysDescription ("Returns whether the table has errors.")]
\r
338 public bool HasErrors {
\r
340 // we can not use the _hasError flag because we do not know when to turn it off!
\r
341 for (int i = 0; i < _rows.Count; i++)
\r
343 if (_rows[i].HasErrors)
\r
351 /// Gets or sets the locale information used to
\r
352 /// compare strings within the table.
\r
354 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
\r
355 public CultureInfo Locale {
\r
357 // if the locale is null, we check for the DataSet locale
\r
358 // and if the DataSet is null we return the current culture.
\r
359 // this way if DataSet locale is changed, only if there is no locale for
\r
360 // the DataTable it influece the Locale get;
\r
361 if (_locale != null)
\r
363 if (DataSet != null)
\r
364 return DataSet.Locale;
\r
365 return CultureInfo.CurrentCulture;
\r
368 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
369 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
371 if (_locale == null || !_locale.Equals(value))
\r
377 /// Gets or sets the initial starting size for this table.
\r
379 [DataCategory ("Data")]
\r
380 [DataSysDescription ("Indicates an initial starting size for this table.")]
\r
381 [DefaultValue (50)]
\r
382 public int MinimumCapacity {
\r
383 get { return _minimumCapacity; }
\r
384 set { _minimumCapacity = value; }
\r
388 /// Gets or sets the namespace for the XML represenation
\r
389 /// of the data stored in the DataTable.
\r
391 [DataCategory ("Data")]
\r
392 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
\r
393 public string Namespace {
\r
396 if (_nameSpace != null)
\r
400 if (DataSet != null)
\r
402 return DataSet.Namespace;
\r
404 return String.Empty;
\r
406 set { _nameSpace = value; }
\r
410 /// Gets the collection of parent relations for
\r
411 /// this DataTable.
\r
413 [Browsable (false)]
\r
414 [DataSysDescription ("Returns the parent relations for this table.")]
\r
415 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
416 public DataRelationCollection ParentRelations {
\r
418 return _parentRelations;
\r
423 /// Gets or sets the namespace for the XML represenation
\r
424 /// of the data stored in the DataTable.
\r
426 [DataCategory ("Data")]
\r
427 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
\r
428 [DefaultValue ("")]
\r
429 public string Prefix {
\r
430 get { return _prefix == null ? "" : _prefix; }
\r
432 // Prefix cannot contain any special characters other than '_' and ':'
\r
433 for (int i = 0; i < value.Length; i++) {
\r
434 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
\r
435 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
\r
442 /// Gets or sets an array of columns that function as
\r
443 /// primary keys for the data table.
\r
445 [DataCategory ("Data")]
\r
446 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
\r
447 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
448 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
\r
449 public DataColumn[] PrimaryKey {
\r
451 if (_primaryKeyConstraint == null) {
\r
452 return new DataColumn[] {};
\r
454 return _primaryKeyConstraint.Columns;
\r
457 UniqueConstraint oldPKConstraint = _primaryKeyConstraint;
\r
459 // first check if value is the same as current PK.
\r
460 if (oldPKConstraint != null && DataColumn.AreColumnSetsTheSame(value, oldPKConstraint.Columns))
\r
463 // remove PK Constraint
\r
464 if(oldPKConstraint != null) {
\r
465 _primaryKeyConstraint = null;
\r
466 Constraints.Remove(oldPKConstraint);
\r
469 if (value != null) {
\r
470 //Does constraint exist for these columns
\r
471 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(this.Constraints, (DataColumn[]) value);
\r
473 //if constraint doesn't exist for columns
\r
474 //create new unique primary key constraint
\r
476 foreach (DataColumn Col in (DataColumn[]) value) {
\r
477 if (Col.Table == null)
\r
480 if (Columns.IndexOf (Col) < 0)
\r
481 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
\r
483 // create constraint with primary key indication set to false
\r
484 // to avoid recursion
\r
485 uc = new UniqueConstraint( (DataColumn[]) value, false);
\r
486 Constraints.Add (uc);
\r
489 //set the constraint as the new primary key
\r
490 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
\r
491 _primaryKeyConstraint = uc;
\r
496 internal UniqueConstraint PrimaryKeyConstraint {
\r
498 return _primaryKeyConstraint;
\r
503 /// Gets the collection of_rows that belong to this table.
\r
505 [Browsable (false)]
\r
506 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
\r
507 public DataRowCollection Rows {
\r
508 get { return _rows; }
\r
512 /// Gets or sets an System.ComponentModel.ISite
\r
513 /// for the DataTable.
\r
515 [Browsable (false)]
\r
516 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
517 public override ISite Site {
\r
518 get { return _site; }
\r
519 set { _site = value; }
\r
523 /// Gets or sets the name of the the DataTable.
\r
525 [DataCategory ("Data")]
\r
526 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
\r
527 [DefaultValue ("")]
\r
528 [RefreshProperties (RefreshProperties.All)]
\r
529 public string TableName {
\r
530 get { return _tableName == null ? "" : _tableName; }
\r
531 set { _tableName = value; }
\r
534 bool IListSource.ContainsListCollection {
\r
536 // the collection is a DataView
\r
541 internal RecordCache RecordCache {
\r
543 return _recordCache;
\r
547 private DataRowBuilder RowBuilder
\r
551 // initiate only one row builder.
\r
552 if (_rowBuilder == null)
\r
553 _rowBuilder = new DataRowBuilder (this, -1, 0);
\r
555 // new row get id -1.
\r
556 _rowBuilder._rowId = -1;
\r
558 return _rowBuilder;
\r
562 private bool EnforceConstraints {
\r
563 get { return enforceConstraints; }
\r
565 if (value != enforceConstraints) {
\r
567 // reset indexes since they may be outdated
\r
570 bool violatesConstraints = false;
\r
572 //FIXME: use index for AllowDBNull
\r
573 for (int i = 0; i < Columns.Count; i++) {
\r
574 DataColumn column = Columns[i];
\r
575 if (!column.AllowDBNull) {
\r
576 for (int j = 0; j < Rows.Count; j++){
\r
577 if (Rows[j].IsNull(column)) {
\r
578 violatesConstraints = true;
\r
579 Rows[j].RowError = String.Format("Column '{0}' does not allow DBNull.Value.", column.ColumnName);
\r
585 if (violatesConstraints)
\r
586 Constraint.ThrowConstraintException();
\r
588 // assert all constraints
\r
589 foreach (Constraint constraint in Constraints) {
\r
590 constraint.AssertConstraint();
\r
593 enforceConstraints = value;
\r
598 internal bool RowsExist(DataColumn[] columns, DataColumn[] relatedColumns,DataRow row)
\r
600 int curIndex = row.IndexFromVersion(DataRowVersion.Default);
\r
601 int tmpRecord = RecordCache.NewRecord();
\r
604 for (int i = 0; i < relatedColumns.Length; i++) {
\r
605 // according to MSDN: the DataType value for both columns must be identical.
\r
606 columns[i].DataContainer.CopyValue(relatedColumns[i].DataContainer, curIndex, tmpRecord);
\r
608 return RowsExist(columns, tmpRecord);
\r
611 RecordCache.DisposeRecord(tmpRecord);
\r
615 bool RowsExist(DataColumn[] columns, int index)
\r
617 bool rowsExist = false;
\r
618 Index indx = this.FindIndex(columns);
\r
620 if (indx != null) { // lookup for a row in index
\r
621 rowsExist = (indx.Find(index) != -1);
\r
624 // we have to perform full-table scan
\r
625 // check that there is a parent for this row.
\r
626 foreach (DataRow thisRow in this.Rows) {
\r
627 if (thisRow.RowState != DataRowState.Deleted) {
\r
629 // check if the values in the columns are equal
\r
630 int thisIndex = thisRow.IndexFromVersion(DataRowVersion.Current);
\r
631 foreach (DataColumn column in columns) {
\r
632 if (column.DataContainer.CompareValues(thisIndex, index) != 0) {
\r
637 if (match) {// there is a row with columns values equals to those supplied.
\r
648 /// Commits all the changes made to this table since the
\r
649 /// last time AcceptChanges was called.
\r
651 public void AcceptChanges ()
\r
653 //FIXME: Do we need to validate anything here or
\r
654 //try to catch any errors to deal with them?
\r
656 // we do not use foreach because if one of the rows is in Delete state
\r
657 // it will be romeved from Rows and we get an exception.
\r
659 for (int i = 0; i < Rows.Count; )
\r
662 myRow.AcceptChanges();
\r
664 // if the row state is Detached it meens that it was removed from row list (Rows)
\r
665 // so we should not increase 'i'.
\r
666 if (myRow.RowState != DataRowState.Detached)
\r
672 /// Begins the initialization of a DataTable that is used
\r
673 /// on a form or used by another component. The initialization
\r
674 /// occurs at runtime.
\r
676 public virtual void BeginInit ()
\r
678 fInitInProgress = true;
\r
682 /// Turns off notifications, index maintenance, and
\r
683 /// constraints while loading data.
\r
686 public void BeginLoadData ()
\r
688 if (!this._duringDataLoad)
\r
690 //duringDataLoad is important to EndLoadData and
\r
691 //for not throwing unexpected exceptions.
\r
692 this._duringDataLoad = true;
\r
693 this._nullConstraintViolationDuringDataLoad = false;
\r
695 if (this.dataSet != null)
\r
697 //Saving old Enforce constraints state for later
\r
698 //use in the EndLoadData.
\r
699 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
\r
700 this.dataSet.EnforceConstraints = false;
\r
703 //if table does not belong to any data set use EnforceConstraints of the table
\r
704 this.EnforceConstraints = false;
\r
711 /// Clears the DataTable of all data.
\r
713 public void Clear () {
\r
714 // Foriegn key constraints are checked in _rows.Clear method
\r
717 OnTableCleared (new DataTableClearEventArgs (this));
\r
723 /// Clones the structure of the DataTable, including
\r
724 /// all DataTable schemas and constraints.
\r
726 public virtual DataTable Clone ()
\r
728 // Use Activator so we can use non-public constructors.
\r
729 DataTable Copy = (DataTable) Activator.CreateInstance(GetType(), true);
\r
730 CopyProperties (Copy);
\r
735 /// Computes the given expression on the current_rows that
\r
736 /// pass the filter criteria.
\r
739 public object Compute (string expression, string filter)
\r
741 // expression is an aggregate function
\r
742 // filter is an expression used to limit rows
\r
744 DataRow[] rows = Select(filter);
\r
746 if (rows == null || rows.Length == 0)
\r
747 return DBNull.Value;
\r
749 Parser parser = new Parser (rows);
\r
750 IExpression expr = parser.Compile (expression);
\r
751 object obj = expr.Eval (rows[0]);
\r
757 /// Copies both the structure and data for this DataTable.
\r
759 public DataTable Copy ()
\r
761 DataTable copy = Clone();
\r
763 copy._duringDataLoad = true;
\r
764 foreach (DataRow row in Rows) {
\r
765 DataRow newRow = copy.NewNotInitializedRow();
\r
766 copy.Rows.AddInternal(newRow);
\r
767 CopyRow(row,newRow);
\r
769 copy._duringDataLoad = false;
\r
771 // rebuild copy indexes after loading all rows
\r
772 copy.ResetIndexes();
\r
776 internal void CopyRow(DataRow fromRow,DataRow toRow)
\r
778 if (fromRow.HasErrors) {
\r
779 fromRow.CopyErrors(toRow);
\r
782 if (fromRow.HasVersion(DataRowVersion.Original)) {
\r
783 toRow.Original = toRow.Table.RecordCache.CopyRecord(this,fromRow.Original,-1);
\r
786 if (fromRow.HasVersion(DataRowVersion.Current)) {
\r
787 if (fromRow.Original != fromRow.Current) {
\r
788 toRow.Current = toRow.Table.RecordCache.CopyRecord(this,fromRow.Current,-1);
\r
791 toRow.Current = toRow.Original;
\r
796 private void CopyProperties (DataTable Copy)
\r
798 Copy.CaseSensitive = CaseSensitive;
\r
799 Copy._virginCaseSensitive = _virginCaseSensitive;
\r
801 // Copy.ChildRelations
\r
802 // Copy.Constraints
\r
804 // Copy.DefaultView
\r
806 Copy.DisplayExpression = DisplayExpression;
\r
807 if(ExtendedProperties.Count > 0) {
\r
808 // Cannot copy extended properties directly as the property does not have a set accessor
\r
809 Array tgtArray = Array.CreateInstance( typeof (object), ExtendedProperties.Count);
\r
810 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
\r
811 for (int i=0; i < ExtendedProperties.Count; i++)
\r
812 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
\r
814 Copy.Locale = Locale;
\r
815 Copy.MinimumCapacity = MinimumCapacity;
\r
816 Copy.Namespace = Namespace;
\r
817 // Copy.ParentRelations
\r
818 Copy.Prefix = Prefix;
\r
820 Copy.TableName = TableName;
\r
822 bool isEmpty = Copy.Columns.Count == 0;
\r
825 foreach (DataColumn column in Columns) {
\r
826 // When cloning a table, the columns may be added in the default constructor.
\r
827 if (isEmpty || !Copy.Columns.Contains(column.ColumnName)) {
\r
828 Copy.Columns.Add (column.Clone());
\r
832 CopyConstraints(Copy);
\r
833 // add primary key to the copy
\r
834 if (PrimaryKey.Length > 0) {
\r
835 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
\r
836 for (int i = 0; i < pColumns.Length; i++)
\r
837 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
\r
839 Copy.PrimaryKey = pColumns;
\r
843 private void CopyConstraints(DataTable copy)
\r
845 UniqueConstraint origUc;
\r
846 UniqueConstraint copyUc;
\r
847 for (int i = 0; i < this.Constraints.Count; i++)
\r
849 if (this.Constraints[i] is UniqueConstraint)
\r
851 origUc = (UniqueConstraint)this.Constraints[i];
\r
852 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
\r
853 for (int j = 0; j < columns.Length; j++)
\r
854 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
\r
856 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
\r
857 copy.Constraints.Add(copyUc);
\r
862 /// Ends the initialization of a DataTable that is used
\r
863 /// on a form or used by another component. The
\r
864 /// initialization occurs at runtime.
\r
867 public virtual void EndInit ()
\r
869 fInitInProgress = false;
\r
870 // Add the constraints
\r
871 _constraintCollection.PostEndInit();
\r
872 Columns.PostEndInit();
\r
876 /// Turns on notifications, index maintenance, and
\r
877 /// constraints after loading data.
\r
879 public void EndLoadData()
\r
881 if (this._duringDataLoad) {
\r
882 if(this._nullConstraintViolationDuringDataLoad) {
\r
883 this._nullConstraintViolationDuringDataLoad = false;
\r
884 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
\r
887 if (this.dataSet !=null) {
\r
888 //Getting back to previous EnforceConstraint state
\r
889 this.dataSet.InternalEnforceConstraints(this.dataSetPrevEnforceConstraints,true);
\r
892 //Getting back to the table's previous EnforceConstraint state
\r
893 this.EnforceConstraints = true;
\r
896 //Returning from loading mode, raising exceptions as usual
\r
897 this._duringDataLoad = false;
\r
902 /// Gets a copy of the DataTable that contains all
\r
903 /// changes made to it since it was loaded or
\r
904 /// AcceptChanges was last called.
\r
906 public DataTable GetChanges()
\r
908 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
\r
912 /// Gets a copy of the DataTable containing all
\r
913 /// changes made to it since it was last loaded, or
\r
914 /// since AcceptChanges was called, filtered by DataRowState.
\r
916 public DataTable GetChanges(DataRowState rowStates)
\r
918 DataTable copyTable = null;
\r
920 IEnumerator rowEnumerator = Rows.GetEnumerator();
\r
921 while (rowEnumerator.MoveNext()) {
\r
922 DataRow row = (DataRow)rowEnumerator.Current;
\r
923 // The spec says relationship constraints may cause Unchanged parent rows to be included but
\r
924 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
\r
925 if (row.IsRowChanged(rowStates)) {
\r
926 if (copyTable == null)
\r
927 copyTable = Clone();
\r
928 DataRow newRow = copyTable.NewNotInitializedRow();
\r
929 row.CopyValuesToRow(newRow);
\r
930 copyTable.Rows.AddInternal (newRow);
\r
939 public DataTableReader GetDataReader ()
\r
941 throw new NotImplementedException ();
\r
946 /// Gets an array of DataRow objects that contain errors.
\r
948 public DataRow[] GetErrors ()
\r
950 ArrayList errors = new ArrayList();
\r
951 for (int i = 0; i < _rows.Count; i++)
\r
953 if (_rows[i].HasErrors)
\r
954 errors.Add(_rows[i]);
\r
957 DataRow[] ret = NewRowArray(errors.Count);
\r
958 errors.CopyTo(ret, 0);
\r
963 /// This member is only meant to support Mono's infrastructure
\r
965 protected virtual DataTable CreateInstance ()
\r
967 return Activator.CreateInstance (this.GetType (), true) as DataTable;
\r
971 /// This member is only meant to support Mono's infrastructure
\r
973 protected virtual Type GetRowType ()
\r
975 return typeof (DataRow);
\r
979 /// This member is only meant to support Mono's infrastructure
\r
981 /// Used for Data Binding between System.Web.UI. controls
\r
982 /// like a DataGrid
\r
984 /// System.Windows.Forms controls like a DataGrid
\r
986 IList IListSource.GetList ()
\r
988 IList list = (IList) DefaultView;
\r
993 /// Copies a DataRow into a DataTable, preserving any
\r
994 /// property settings, as well as original and current values.
\r
996 public void ImportRow (DataRow row)
\r
998 DataRow newRow = NewNotInitializedRow();
\r
1000 int original = -1;
\r
1001 if (row.HasVersion(DataRowVersion.Original)) {
\r
1002 original = row.IndexFromVersion(DataRowVersion.Original);
\r
1003 newRow.Original = RecordCache.NewRecord();
\r
1004 RecordCache.CopyRecord(row.Table,original,newRow.Original);
\r
1007 if (row.HasVersion(DataRowVersion.Current)) {
\r
1008 int current = row.IndexFromVersion(DataRowVersion.Current);
\r
1009 if (current == original)
\r
1010 newRow.Current = newRow.Original;
\r
1012 newRow.Current = RecordCache.NewRecord();
\r
1013 RecordCache.CopyRecord(row.Table,current,newRow.Current);
\r
1017 if (EnforceConstraints)
\r
1018 // we have to check that the new row doesn't colide with existing row
\r
1019 Rows.ValidateDataRowInternal(newRow);
\r
1021 Rows.AddInternal(newRow);
\r
1023 if (row.HasErrors) {
\r
1024 row.CopyErrors(newRow);
\r
1028 internal int DefaultValuesRowIndex
\r
1031 return _defaultValuesRowIndex;
\r
1036 /// This member is only meant to support Mono's infrastructure
\r
1038 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
\r
1041 if (dataSet != null)
\r
1044 dset = new DataSet ("tmpDataSet");
\r
1045 dset.Tables.Add (this);
\r
1048 StringWriter sw = new StringWriter ();
\r
1049 XmlTextWriter tw = new XmlTextWriter (sw);
\r
1050 tw.Formatting = Formatting.Indented;
\r
1051 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
\r
1054 StringWriter sw2 = new StringWriter ();
\r
1055 DataTableCollection tables = new DataTableCollection (dset);
\r
1056 tables.Add (this);
\r
1057 XmlSchemaWriter.WriteXmlSchema (dset, new XmlTextWriter (sw2), tables, null);
\r
1060 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
\r
1061 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
\r
1066 /// Loads the table with the values from the reader
\r
1068 public void Load (IDataReader reader)
\r
1070 Load (reader, LoadOption.PreserveChanges);
\r
1074 /// Loads the table with the values from the reader and the pattern
\r
1075 /// of the changes to the existing rows or the new rows are based on
\r
1076 /// the LoadOption passed.
\r
1078 public void Load (IDataReader reader, LoadOption loadOption)
\r
1080 bool prevEnforceConstr = this.EnforceConstraints;
\r
1082 this.EnforceConstraints = false;
\r
1083 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,
\r
1084 MissingSchemaAction.AddWithKey,
\r
1085 MissingMappingAction.Passthrough,
\r
1086 new DataTableMappingCollection ());
\r
1087 DbDataAdapter.FillFromReader (this,
\r
1094 this.EnforceConstraints = prevEnforceConstr;
\r
1102 /// Finds and updates a specific row. If no matching row
\r
1103 /// is found, a new row is created using the given values.
\r
1105 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
\r
1107 DataRow row = null;
\r
1108 if (PrimaryKey.Length == 0) {
\r
1109 row = Rows.Add (values);
\r
1110 if (fAcceptChanges)
\r
1111 row.AcceptChanges ();
\r
1114 int newRecord = CreateRecord(values);
\r
1115 int existingRecord = _primaryKeyConstraint.Index.Find(newRecord);
\r
1117 if (existingRecord < 0) {
\r
1118 row = NewRowFromBuilder (RowBuilder);
\r
1119 row.Proposed = newRecord;
\r
1120 Rows.AddInternal(row);
\r
1123 row = RecordCache[existingRecord];
\r
1125 row.ImportRecord(newRecord);
\r
1130 if (fAcceptChanges)
\r
1131 row.AcceptChanges ();
\r
1137 internal DataRow LoadDataRow(IDataRecord record, int[] mapping, int length, bool fAcceptChanges)
\r
1139 DataRow row = null;
\r
1140 int tmpRecord = this.RecordCache.NewRecord();
\r
1142 RecordCache.ReadIDataRecord(tmpRecord,record,mapping,length);
\r
1143 if (PrimaryKey.Length != 0) {
\r
1144 bool hasPrimaryValues = true;
\r
1145 foreach(DataColumn col in PrimaryKey) {
\r
1146 if(!(col.Ordinal < mapping.Length)) {
\r
1147 hasPrimaryValues = false;
\r
1152 if (hasPrimaryValues) {
\r
1153 // find the row in the table.
\r
1154 row = Rows.Find(tmpRecord);
\r
1158 bool shouldUpdateIndex = false;
\r
1159 if (row == null) {
\r
1160 row = NewNotInitializedRow();
\r
1161 row.ImportRecord(tmpRecord);
\r
1162 Rows.AddInternal (row);
\r
1163 shouldUpdateIndex = true;
\r
1166 // Proposed = tmpRecord
\r
1167 row.ImportRecord(tmpRecord);
\r
1170 if (fAcceptChanges) {
\r
1171 row.AcceptChanges();
\r
1174 if (shouldUpdateIndex || !fAcceptChanges) {
\r
1175 // AcceptChanges not always updates indexes because it calls EndEdit
\r
1176 foreach(Index index in Indexes) {
\r
1177 index.Update(row,tmpRecord);
\r
1182 catch(Exception e) {
\r
1183 this.RecordCache.DisposeRecord(tmpRecord);
\r
1191 /// Loads the given values into an existing row if matches or creates
\r
1192 /// the new row popluated with the values.
\r
1195 /// This method searches for the values using primary keys and it first
\r
1196 /// searches using the original values of the rows and if not found, it
\r
1197 /// searches using the current version of the row.
\r
1199 public DataRow LoadDataRow (object [] values, LoadOption loadOption)
\r
1201 DataRow row = null;
\r
1202 bool new_row = false;
\r
1204 // Find Data DataRow
\r
1205 if (this.PrimaryKey.Length > 0) {
\r
1206 object [] keyValues = new object [this.PrimaryKey.Length];
\r
1207 for (int i = 0; i < keyValues.Length; i++)
\r
1208 keyValues [i] = values [this.PrimaryKey [i].Ordinal];
\r
1209 row = this.Rows.Find (keyValues, DataRowVersion.Original );
\r
1211 row = this.Rows.Find (keyValues, DataRowVersion.Current);
\r
1214 // If not found, add new row
\r
1215 if (row == null) {
\r
1216 row = this.NewRow ();
\r
1220 bool deleted = row.RowState == DataRowState.Deleted;
\r
1222 if (deleted && loadOption == LoadOption.OverwriteChanges)
\r
1223 row.RejectChanges ();
\r
1225 row.Load (values, loadOption, new_row);
\r
1227 if (deleted && loadOption == LoadOption.Upsert) {
\r
1228 row = this.NewRow ();
\r
1229 row.Load (values, loadOption, new_row = true);
\r
1233 this.Rows.Add (row);
\r
1234 if (loadOption == LoadOption.OverwriteChanges ||
\r
1235 loadOption == LoadOption.PreserveChanges) {
\r
1236 row.AcceptChanges ();
\r
1244 public void Merge (DataTable table)
\r
1246 throw new NotImplementedException ();
\r
1250 public void Merge (DataTable table, bool preserveChanges)
\r
1252 throw new NotImplementedException ();
\r
1256 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
\r
1258 throw new NotImplementedException ();
\r
1263 /// Creates a new DataRow with the same schema as the table.
\r
1265 public DataRow NewRow ()
\r
1267 EnsureDefaultValueRowIndex();
\r
1269 DataRow newRow = NewRowFromBuilder (RowBuilder);
\r
1271 newRow.Proposed = CreateRecord(null);
\r
1275 internal int CreateRecord(object[] values) {
\r
1276 int valCount = values != null ? values.Length : 0;
\r
1277 if (valCount > Columns.Count)
\r
1278 throw new ArgumentException("Input array is longer than the number of columns in this table.");
\r
1280 int index = RecordCache.NewRecord();
\r
1283 for (int i = 0; i < valCount; i++) {
\r
1285 Columns[i].DataContainer[index] = values[i];
\r
1287 catch(Exception e) {
\r
1288 throw new ArgumentException(e.Message +
\r
1289 String.Format("Couldn't store <{0}> in {1} Column. Expected type is {2}.",
\r
1290 values[i], Columns[i].ColumnName, Columns[i].DataType.Name), e);
\r
1294 for(int i = valCount; i < Columns.Count; i++) {
\r
1295 DataColumn column = Columns[i];
\r
1296 if (column.AutoIncrement)
\r
1297 column.DataContainer[index] = column.AutoIncrementValue ();
\r
1299 column.DataContainer.CopyValue(DefaultValuesRowIndex, index);
\r
1305 RecordCache.DisposeRecord(index);
\r
1310 private void EnsureDefaultValueRowIndex()
\r
1312 // initialize default values row for the first time
\r
1313 if ( _defaultValuesRowIndex == -1 ) {
\r
1314 _defaultValuesRowIndex = RecordCache.NewRecord();
\r
1315 foreach(DataColumn column in Columns) {
\r
1316 column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;
\r
1322 /// This member supports the .NET Framework infrastructure
\r
1323 /// and is not intended to be used directly from your code.
\r
1325 protected internal DataRow[] NewRowArray (int size)
\r
1327 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
\r
1331 /// Creates a new row from an existing row.
\r
1333 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
1335 return new DataRow (builder);
\r
1338 internal DataRow NewNotInitializedRow()
\r
1340 EnsureDefaultValueRowIndex();
\r
1342 return NewRowFromBuilder (RowBuilder);
\r
1347 XmlReadMode ReadXml (Stream stream)
\r
1349 throw new NotImplementedException ();
\r
1352 public void ReadXmlSchema (Stream stream)
\r
1354 ReadXmlSchema (new XmlTextReader (stream));
\r
1357 public void ReadXmlSchema (TextReader reader)
\r
1359 ReadXmlSchema (new XmlTextReader (reader));
\r
1362 public void ReadXmlSchema (string fileName)
\r
1364 XmlTextReader reader = null;
\r
1366 reader = new XmlTextReader (fileName);
\r
1367 ReadXmlSchema (reader);
\r
1369 if (reader != null)
\r
1374 public void ReadXmlSchema (XmlReader reader)
\r
1376 DataSet ds = new DataSet ();
\r
1377 new XmlSchemaDataImporter (ds, reader).Process ();
\r
1378 DataTable target = null;
\r
1379 if (TableName == String.Empty) {
\r
1380 if (ds.Tables.Count > 0)
\r
1381 target = ds.Tables [0];
\r
1384 target = ds.Tables [TableName];
\r
1385 if (target == null)
\r
1386 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));
\r
1388 if (target != null)
\r
1389 target.CopyProperties (this);
\r
1394 /// Rolls back all changes that have been made to the
\r
1395 /// table since it was loaded, or the last time AcceptChanges
\r
1398 public void RejectChanges ()
\r
1400 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
1401 DataRow row = _rows [i];
\r
1402 if (row.RowState != DataRowState.Unchanged)
\r
1403 _rows [i].RejectChanges ();
\r
1408 /// Resets the DataTable to its original state.
\r
1410 public virtual void Reset ()
\r
1413 while (ParentRelations.Count > 0)
\r
1415 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
\r
1416 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
\r
1419 while (ChildRelations.Count > 0)
\r
1421 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
\r
1422 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
\r
1424 Constraints.Clear();
\r
1429 /// Gets an array of all DataRow objects.
\r
1431 public DataRow[] Select ()
\r
1433 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
1437 /// Gets an array of all DataRow objects that match
\r
1438 /// the filter criteria in order of primary key (or
\r
1439 /// lacking one, order of addition.)
\r
1441 public DataRow[] Select (string filterExpression)
\r
1443 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
1447 /// Gets an array of all DataRow objects that
\r
1448 /// match the filter criteria, in the the
\r
1449 /// specified sort order.
\r
1451 public DataRow[] Select (string filterExpression, string sort)
\r
1453 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
\r
1457 /// Gets an array of all DataRow objects that match
\r
1458 /// the filter in the order of the sort, that match
\r
1459 /// the specified state.
\r
1462 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
\r
1464 if (filterExpression == null)
\r
1465 filterExpression = String.Empty;
\r
1467 DataColumn[] columns = _emptyColumnArray;
\r
1468 ListSortDirection[] sorts = null;
\r
1469 if (sort != null && !sort.Equals(String.Empty))
\r
1470 columns = ParseSortString (this, sort, out sorts, false);
\r
1472 IExpression filter = null;
\r
1473 if (filterExpression != String.Empty) {
\r
1474 Parser parser = new Parser ();
\r
1475 filter = parser.Compile (filterExpression);
\r
1478 Index index = FindIndex(columns, sorts, recordStates, filter);
\r
1479 if (index == null)
\r
1480 index = new Index(new Key(this,columns,sorts,recordStates,filter));
\r
1482 int[] records = index.GetAll();
\r
1483 DataRow[] dataRows = NewRowArray(index.Size);
\r
1484 for (int i = 0; i < dataRows.Length; i++)
\r
1485 dataRows[i] = RecordCache[records[i]];
\r
1491 private void AddIndex (Index index)
\r
1493 if (_indexes == null) {
\r
1494 _indexes = new ArrayList();
\r
1497 _indexes.Add (index);
\r
1501 /// Returns index corresponding to columns,sort,row state filter and unique values given.
\r
1502 /// If such an index not exists, creates a new one.
\r
1504 /// <param name="columns">Columns set of the index to look for.</param>
\r
1505 /// <param name="sort">Columns sort order of the index to look for.</param>
\r
1506 /// <param name="rowState">Rpw state filter of the index to look for.</param>
\r
1507 /// <param name="unique">Uniqueness of the index to look for.</param>
\r
1508 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>
\r
1509 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>
\r
1510 /// <returns></returns>
\r
1511 internal Index GetIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)
\r
1513 Index index = FindIndex(columns,sort,rowState,filter);
\r
1514 if (index == null ) {
\r
1515 index = new Index(new Key(this,columns,sort,rowState,filter));
\r
1520 // reset existing index only if asked for this
\r
1526 internal Index FindIndex(DataColumn[] columns)
\r
1528 return FindIndex(columns,null,DataViewRowState.None, null);
\r
1531 internal Index FindIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)
\r
1533 if (Indexes != null) {
\r
1534 foreach (Index index in Indexes) {
\r
1535 if (index.Key.Equals(columns,sort,rowState, filter)) {
\r
1543 internal void ResetIndexes()
\r
1545 foreach(Index index in Indexes) {
\r
1550 internal void ResetCaseSensitiveIndexes()
\r
1552 foreach(Index index in Indexes) {
\r
1553 bool containsStringcolumns = false;
\r
1554 foreach(DataColumn column in index.Key.Columns) {
\r
1555 if (column.DataType == typeof(string)) {
\r
1556 containsStringcolumns = true;
\r
1561 if (containsStringcolumns) {
\r
1567 internal void DropIndex(Index index)
\r
1569 if (index != null && index.RefCount == 0) {
\r
1570 _indexes.Remove(index);
\r
1574 internal void DeleteRowFromIndexes (DataRow row)
\r
1576 if (_indexes != null) {
\r
1577 foreach (Index indx in _indexes) {
\r
1578 indx.Delete (row);
\r
1584 /// Gets the TableName and DisplayExpression, if
\r
1585 /// there is one as a concatenated string.
\r
1587 public override string ToString()
\r
1589 //LAMESPEC: spec says concat the two. impl puts a
\r
1590 //plus sign infront of DisplayExpression
\r
1591 string retVal = TableName;
\r
1592 if(DisplayExpression != null && DisplayExpression != "")
\r
1593 retVal += " + " + DisplayExpression;
\r
1598 private XmlWriterSettings GetWriterSettings ()
\r
1600 XmlWriterSettings s = new XmlWriterSettings ();
\r
1605 public void WriteXml (Stream stream)
\r
1607 WriteXml (stream, XmlWriteMode.IgnoreSchema);
\r
1610 public void WriteXml (TextWriter writer)
\r
1612 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1615 public void WriteXml (XmlWriter writer)
\r
1617 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1620 public void WriteXml (string fileName)
\r
1622 WriteXml (fileName, XmlWriteMode.IgnoreSchema);
\r
1625 public void WriteXml (Stream stream, XmlWriteMode mode)
\r
1627 WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode);
\r
1630 public void WriteXml (TextWriter writer, XmlWriteMode mode)
\r
1632 WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode);
\r
1636 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
\r
1638 throw new NotImplementedException ();
\r
1641 public void WriteXml (string fileName, XmlWriteMode mode)
\r
1643 XmlWriter xw = null;
\r
1645 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1646 WriteXml (xw, mode);
\r
1653 public void WriteXmlSchema (Stream stream)
\r
1655 WriteXmlSchema (XmlWriter.Create (stream, GetWriterSettings ()));
\r
1658 public void WriteXmlSchema (TextWriter writer)
\r
1660 WriteXmlSchema (XmlWriter.Create (writer, GetWriterSettings ()));
\r
1663 public void WriteXmlSchema (XmlWriter writer)
\r
1665 DataSet ds = DataSet;
\r
1666 DataSet tmp = null;
\r
1669 tmp = ds = new DataSet ();
\r
1670 ds.Tables.Add (this);
\r
1672 DataTableCollection col = new DataTableCollection (ds);
\r
1674 XmlSchemaWriter.WriteXmlSchema (ds, writer, col, null);
\r
1677 ds.Tables.Remove (this);
\r
1681 public void WriteXmlSchema (string fileName)
\r
1683 XmlWriter xw = null;
\r
1685 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1686 WriteXmlSchema (xw);
\r
1697 /// Raises the ColumnChanged event.
\r
1699 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
\r
1700 if (null != ColumnChanged) {
\r
1701 ColumnChanged (this, e);
\r
1705 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {
\r
1706 OnColumnChanged(e);
\r
1711 /// Raises TableCleared Event and delegates to subscribers
\r
1713 protected virtual void OnTableCleared (DataTableClearEventArgs e) {
\r
1714 if (TableCleared != null)
\r
1715 TableCleared (this, e);
\r
1720 /// Raises the ColumnChanging event.
\r
1722 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
\r
1723 if (null != ColumnChanging) {
\r
1724 ColumnChanging (this, e);
\r
1728 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e) {
\r
1729 OnColumnChanging(e);
\r
1733 /// Raises the PropertyChanging event.
\r
1736 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
\r
1737 // if (null != PropertyChanging)
\r
1739 // PropertyChanging (this, e);
\r
1744 /// Notifies the DataTable that a DataColumn is being removed.
\r
1747 protected internal virtual void OnRemoveColumn (DataColumn column) {
\r
1752 /// Raises the RowChanged event.
\r
1754 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
\r
1755 if (null != RowChanged) {
\r
1756 RowChanged(this, e);
\r
1762 /// Raises the RowChanging event.
\r
1764 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
\r
1765 if (null != RowChanging) {
\r
1766 RowChanging(this, e);
\r
1771 /// Raises the RowDeleted event.
\r
1773 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
\r
1774 if (null != RowDeleted) {
\r
1775 RowDeleted(this, e);
\r
1780 /// Raises the RowDeleting event.
\r
1782 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
\r
1783 if (null != RowDeleting) {
\r
1784 RowDeleting(this, e);
\r
1789 /// Occurs when after a value has been changed for
\r
1790 /// the specified DataColumn in a DataRow.
\r
1792 [DataCategory ("Data")]
\r
1793 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1794 public event DataColumnChangeEventHandler ColumnChanged;
\r
1797 /// Occurs when a value is being changed for the specified
\r
1798 /// DataColumn in a DataRow.
\r
1800 [DataCategory ("Data")]
\r
1801 [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
1802 public event DataColumnChangeEventHandler ColumnChanging;
\r
1805 /// Occurs after a DataRow has been changed successfully.
\r
1807 [DataCategory ("Data")]
\r
1808 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1809 public event DataRowChangeEventHandler RowChanged;
\r
1812 /// Occurs when a DataRow is changing.
\r
1814 [DataCategory ("Data")]
\r
1815 [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
1816 public event DataRowChangeEventHandler RowChanging;
\r
1819 /// Occurs after a row in the table has been deleted.
\r
1821 [DataCategory ("Data")]
\r
1822 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1823 public event DataRowChangeEventHandler RowDeleted;
\r
1826 /// Occurs before a row in the table is about to be deleted.
\r
1828 [DataCategory ("Data")]
\r
1829 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1830 public event DataRowChangeEventHandler RowDeleting;
\r
1834 /// Occurs after the Clear method is called on the datatable.
\r
1836 [DataCategory ("Data")]
\r
1837 [DataSysDescription ("Occurs when the rows in a table is cleared . Throw an exception to cancel the deletion.")]
\r
1838 public event DataTableClearEventHandler TableCleared;
\r
1841 #endregion // Events
\r
1844 /// Removes all UniqueConstraints
\r
1846 private void RemoveUniqueConstraints ()
\r
1848 foreach (Constraint Cons in Constraints) {
\r
1850 if (Cons is UniqueConstraint) {
\r
1851 Constraints.Remove (Cons);
\r
1856 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
\r
1859 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)
\r
1861 DataColumn[] sortColumns = _emptyColumnArray;
\r
1862 sortDirections = null;
\r
1864 ArrayList columns = null;
\r
1865 ArrayList sorts = null;
\r
1867 if (sort != null && !sort.Equals ("")) {
\r
1868 columns = new ArrayList ();
\r
1869 sorts = new ArrayList();
\r
1870 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1872 for (int c = 0; c < columnExpression.Length; c++) {
\r
1873 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
1875 string columnName = columnSortInfo[0].Trim ();
\r
1876 string sortOrder = "ASC";
\r
1877 if (columnSortInfo.Length > 1)
\r
1878 sortOrder = columnSortInfo[1].Trim ().ToUpper (table.Locale);
\r
1880 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
1881 switch (sortOrder) {
\r
1883 sortDirection = ListSortDirection.Ascending;
\r
1886 sortDirection = ListSortDirection.Descending;
\r
1889 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
1892 if (columnName.StartsWith("[") || columnName.EndsWith("]")) {
\r
1893 if (columnName.StartsWith("[") && columnName.EndsWith("]"))
\r
1894 columnName = columnName.Substring(1, columnName.Length - 2);
\r
1896 throw new ArgumentException(String.Format("{0} isn't a valid Sort string entry.", columnName));
\r
1899 DataColumn dc = table.Columns[columnName];
\r
1902 dc = table.Columns[Int32.Parse (columnName)];
\r
1904 catch (FormatException) {
\r
1905 throw new IndexOutOfRangeException("Cannot find column " + columnName);
\r
1910 sorts.Add(sortDirection);
\r
1912 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));
\r
1913 sortDirections = new ListSortDirection[sorts.Count];
\r
1914 for (int i = 0; i < sortDirections.Length; i++)
\r
1915 sortDirections[i] = (ListSortDirection)sorts[i];
\r
1918 if (rejectNoResult) {
\r
1919 if (sortColumns == null)
\r
1920 throw new SystemException ("sort expression result is null");
\r
1921 if (sortColumns.Length == 0)
\r
1922 throw new SystemException("sort expression result is 0");
\r
1925 return sortColumns;
\r
1928 private void UpdatePropertyDescriptorsCache()
\r
1930 PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];
\r
1932 foreach(DataColumn col in Columns) {
\r
1933 descriptors[index++] = new DataColumnPropertyDescriptor(col);
\r
1936 foreach(DataRelation rel in ChildRelations) {
\r
1937 descriptors[index++] = new DataRelationPropertyDescriptor(rel);
\r
1940 _propertyDescriptorsCache = new PropertyDescriptorCollection(descriptors);
\r
1943 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()
\r
1945 if (_propertyDescriptorsCache == null) {
\r
1946 UpdatePropertyDescriptorsCache();
\r
1949 return _propertyDescriptorsCache;
\r
1952 internal void ResetPropertyDescriptorsCache() {
\r
1953 _propertyDescriptorsCache = null;
\r