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
1203 // Find Data DataRow
\r
1204 if (this.PrimaryKey.Length > 0) {
\r
1205 object [] keys = new object [PrimaryKey.Length];
\r
1206 for (int i=0; i < PrimaryKey.Length; i++)
\r
1207 keys [i] = values [PrimaryKey [i].Ordinal];
\r
1209 row = Rows.Find(keys, DataViewRowState.OriginalRows);
\r
1211 row = Rows.Find (keys);
\r
1214 // If not found, add new row
\r
1216 || (row.RowState == DataRowState.Deleted
\r
1217 && loadOption == LoadOption.Upsert)) {
\r
1218 row = NewNotInitializedRow ();
\r
1219 row.ImportRecord (CreateRecord(values));
\r
1221 if (EnforceConstraints)
\r
1222 // we have to check that the new row doesn't colide with existing row
\r
1223 Rows.ValidateDataRowInternal(row); // this adds to index ;-)
\r
1225 Rows.AddInternal(row);
\r
1227 if (loadOption == LoadOption.OverwriteChanges ||
\r
1228 loadOption == LoadOption.PreserveChanges) {
\r
1229 row.AcceptChanges ();
\r
1234 bool deleted = row.RowState == DataRowState.Deleted;
\r
1236 if (deleted && loadOption == LoadOption.OverwriteChanges)
\r
1237 row.RejectChanges ();
\r
1239 row.Load (values, loadOption);
\r
1245 public void Merge (DataTable table)
\r
1247 throw new NotImplementedException ();
\r
1251 public void Merge (DataTable table, bool preserveChanges)
\r
1253 throw new NotImplementedException ();
\r
1257 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
\r
1259 throw new NotImplementedException ();
\r
1264 /// Creates a new DataRow with the same schema as the table.
\r
1266 public DataRow NewRow ()
\r
1268 EnsureDefaultValueRowIndex();
\r
1270 DataRow newRow = NewRowFromBuilder (RowBuilder);
\r
1272 newRow.Proposed = CreateRecord(null);
\r
1276 internal int CreateRecord(object[] values) {
\r
1277 int valCount = values != null ? values.Length : 0;
\r
1278 if (valCount > Columns.Count)
\r
1279 throw new ArgumentException("Input array is longer than the number of columns in this table.");
\r
1281 int index = RecordCache.NewRecord();
\r
1285 for (int i = 0; i < valCount; i++) {
\r
1286 object value = values[i];
\r
1287 if (value == null)
\r
1288 Columns[i].SetDefaultValue(index);
\r
1290 Columns[i][index] = values[i];
\r
1293 for(int i = valCount; i < Columns.Count; i++) {
\r
1294 Columns[i].SetDefaultValue(index);
\r
1300 RecordCache.DisposeRecord(index);
\r
1305 private void EnsureDefaultValueRowIndex()
\r
1307 // initialize default values row for the first time
\r
1308 if ( _defaultValuesRowIndex == -1 ) {
\r
1309 _defaultValuesRowIndex = RecordCache.NewRecord();
\r
1310 foreach(DataColumn column in Columns) {
\r
1311 column.DataContainer[_defaultValuesRowIndex] = column.DefaultValue;
\r
1317 internal int CompareRecords(int x, int y) {
\r
1318 for (int col = 0; col < Columns.Count; col++) {
\r
1319 int res = Columns[col].DataContainer.CompareValues (x, y);
\r
1329 /// This member supports the .NET Framework infrastructure
\r
1330 /// and is not intended to be used directly from your code.
\r
1332 protected internal DataRow[] NewRowArray (int size)
\r
1334 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
\r
1338 /// Creates a new row from an existing row.
\r
1340 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
1342 return new DataRow (builder);
\r
1345 internal DataRow NewNotInitializedRow()
\r
1347 EnsureDefaultValueRowIndex();
\r
1349 return NewRowFromBuilder (RowBuilder);
\r
1354 XmlReadMode ReadXml (Stream stream)
\r
1356 throw new NotImplementedException ();
\r
1359 public void ReadXmlSchema (Stream stream)
\r
1361 ReadXmlSchema (new XmlTextReader (stream));
\r
1364 public void ReadXmlSchema (TextReader reader)
\r
1366 ReadXmlSchema (new XmlTextReader (reader));
\r
1369 public void ReadXmlSchema (string fileName)
\r
1371 XmlTextReader reader = null;
\r
1373 reader = new XmlTextReader (fileName);
\r
1374 ReadXmlSchema (reader);
\r
1376 if (reader != null)
\r
1381 public void ReadXmlSchema (XmlReader reader)
\r
1383 DataSet ds = new DataSet ();
\r
1384 new XmlSchemaDataImporter (ds, reader).Process ();
\r
1385 DataTable target = null;
\r
1386 if (TableName == String.Empty) {
\r
1387 if (ds.Tables.Count > 0)
\r
1388 target = ds.Tables [0];
\r
1391 target = ds.Tables [TableName];
\r
1392 if (target == null)
\r
1393 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));
\r
1395 if (target != null)
\r
1396 target.CopyProperties (this);
\r
1401 /// Rolls back all changes that have been made to the
\r
1402 /// table since it was loaded, or the last time AcceptChanges
\r
1405 public void RejectChanges ()
\r
1407 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
1408 DataRow row = _rows [i];
\r
1409 if (row.RowState != DataRowState.Unchanged)
\r
1410 _rows [i].RejectChanges ();
\r
1415 /// Resets the DataTable to its original state.
\r
1417 public virtual void Reset ()
\r
1420 while (ParentRelations.Count > 0)
\r
1422 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
\r
1423 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
\r
1426 while (ChildRelations.Count > 0)
\r
1428 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
\r
1429 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
\r
1431 Constraints.Clear();
\r
1436 /// Gets an array of all DataRow objects.
\r
1438 public DataRow[] Select ()
\r
1440 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
1444 /// Gets an array of all DataRow objects that match
\r
1445 /// the filter criteria in order of primary key (or
\r
1446 /// lacking one, order of addition.)
\r
1448 public DataRow[] Select (string filterExpression)
\r
1450 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
1454 /// Gets an array of all DataRow objects that
\r
1455 /// match the filter criteria, in the the
\r
1456 /// specified sort order.
\r
1458 public DataRow[] Select (string filterExpression, string sort)
\r
1460 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
\r
1464 /// Gets an array of all DataRow objects that match
\r
1465 /// the filter in the order of the sort, that match
\r
1466 /// the specified state.
\r
1469 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
\r
1471 if (filterExpression == null)
\r
1472 filterExpression = String.Empty;
\r
1474 DataColumn[] columns = _emptyColumnArray;
\r
1475 ListSortDirection[] sorts = null;
\r
1476 if (sort != null && !sort.Equals(String.Empty))
\r
1477 columns = ParseSortString (this, sort, out sorts, false);
\r
1479 IExpression filter = null;
\r
1480 if (filterExpression != String.Empty) {
\r
1481 Parser parser = new Parser ();
\r
1482 filter = parser.Compile (filterExpression);
\r
1485 Index index = FindIndex(columns, sorts, recordStates, filter);
\r
1486 if (index == null)
\r
1487 index = new Index(new Key(this,columns,sorts,recordStates,filter));
\r
1489 int[] records = index.GetAll();
\r
1490 DataRow[] dataRows = NewRowArray(index.Size);
\r
1491 for (int i = 0; i < dataRows.Length; i++)
\r
1492 dataRows[i] = RecordCache[records[i]];
\r
1498 private void AddIndex (Index index)
\r
1500 if (_indexes == null) {
\r
1501 _indexes = new ArrayList();
\r
1504 _indexes.Add (index);
\r
1508 /// Returns index corresponding to columns,sort,row state filter and unique values given.
\r
1509 /// If such an index not exists, creates a new one.
\r
1511 /// <param name="columns">Columns set of the index to look for.</param>
\r
1512 /// <param name="sort">Columns sort order of the index to look for.</param>
\r
1513 /// <param name="rowState">Rpw state filter of the index to look for.</param>
\r
1514 /// <param name="unique">Uniqueness of the index to look for.</param>
\r
1515 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>
\r
1516 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>
\r
1517 /// <returns></returns>
\r
1518 internal Index GetIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)
\r
1520 Index index = FindIndex(columns,sort,rowState,filter);
\r
1521 if (index == null ) {
\r
1522 index = new Index(new Key(this,columns,sort,rowState,filter));
\r
1527 // reset existing index only if asked for this
\r
1533 internal Index FindIndex(DataColumn[] columns)
\r
1535 return FindIndex(columns,null,DataViewRowState.None, null);
\r
1538 internal Index FindIndex(DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)
\r
1540 if (Indexes != null) {
\r
1541 foreach (Index index in Indexes) {
\r
1542 if (index.Key.Equals(columns,sort,rowState, filter)) {
\r
1550 internal void ResetIndexes()
\r
1552 foreach(Index index in Indexes) {
\r
1557 internal void ResetCaseSensitiveIndexes()
\r
1559 foreach(Index index in Indexes) {
\r
1560 bool containsStringcolumns = false;
\r
1561 foreach(DataColumn column in index.Key.Columns) {
\r
1562 if (column.DataType == typeof(string)) {
\r
1563 containsStringcolumns = true;
\r
1568 if (containsStringcolumns) {
\r
1574 internal void DropIndex(Index index)
\r
1576 if (index != null && index.RefCount == 0) {
\r
1577 _indexes.Remove(index);
\r
1581 internal void DeleteRowFromIndexes (DataRow row)
\r
1583 if (_indexes != null) {
\r
1584 foreach (Index indx in _indexes) {
\r
1585 indx.Delete (row);
\r
1591 /// Gets the TableName and DisplayExpression, if
\r
1592 /// there is one as a concatenated string.
\r
1594 public override string ToString()
\r
1596 //LAMESPEC: spec says concat the two. impl puts a
\r
1597 //plus sign infront of DisplayExpression
\r
1598 string retVal = TableName;
\r
1599 if(DisplayExpression != null && DisplayExpression != "")
\r
1600 retVal += " + " + DisplayExpression;
\r
1605 private XmlWriterSettings GetWriterSettings ()
\r
1607 XmlWriterSettings s = new XmlWriterSettings ();
\r
1612 public void WriteXml (Stream stream)
\r
1614 WriteXml (stream, XmlWriteMode.IgnoreSchema);
\r
1617 public void WriteXml (TextWriter writer)
\r
1619 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1622 public void WriteXml (XmlWriter writer)
\r
1624 WriteXml (writer, XmlWriteMode.IgnoreSchema);
\r
1627 public void WriteXml (string fileName)
\r
1629 WriteXml (fileName, XmlWriteMode.IgnoreSchema);
\r
1632 public void WriteXml (Stream stream, XmlWriteMode mode)
\r
1634 WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode);
\r
1637 public void WriteXml (TextWriter writer, XmlWriteMode mode)
\r
1639 WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode);
\r
1643 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
\r
1645 throw new NotImplementedException ();
\r
1648 public void WriteXml (string fileName, XmlWriteMode mode)
\r
1650 XmlWriter xw = null;
\r
1652 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1653 WriteXml (xw, mode);
\r
1660 public void WriteXmlSchema (Stream stream)
\r
1662 WriteXmlSchema (XmlWriter.Create (stream, GetWriterSettings ()));
\r
1665 public void WriteXmlSchema (TextWriter writer)
\r
1667 WriteXmlSchema (XmlWriter.Create (writer, GetWriterSettings ()));
\r
1670 public void WriteXmlSchema (XmlWriter writer)
\r
1672 DataSet ds = DataSet;
\r
1673 DataSet tmp = null;
\r
1676 tmp = ds = new DataSet ();
\r
1677 ds.Tables.Add (this);
\r
1679 DataTableCollection col = new DataTableCollection (ds);
\r
1681 XmlSchemaWriter.WriteXmlSchema (ds, writer, col, null);
\r
1684 ds.Tables.Remove (this);
\r
1688 public void WriteXmlSchema (string fileName)
\r
1690 XmlWriter xw = null;
\r
1692 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
1693 WriteXmlSchema (xw);
\r
1704 /// Raises the ColumnChanged event.
\r
1706 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
\r
1707 if (null != ColumnChanged) {
\r
1708 ColumnChanged (this, e);
\r
1712 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e) {
\r
1713 OnColumnChanged(e);
\r
1718 /// Raises TableCleared Event and delegates to subscribers
\r
1720 protected virtual void OnTableCleared (DataTableClearEventArgs e) {
\r
1721 if (TableCleared != null)
\r
1722 TableCleared (this, e);
\r
1727 /// Raises the ColumnChanging event.
\r
1729 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
\r
1730 if (null != ColumnChanging) {
\r
1731 ColumnChanging (this, e);
\r
1735 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e) {
\r
1736 OnColumnChanging(e);
\r
1740 /// Raises the PropertyChanging event.
\r
1743 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
\r
1744 // if (null != PropertyChanging)
\r
1746 // PropertyChanging (this, e);
\r
1751 /// Notifies the DataTable that a DataColumn is being removed.
\r
1754 protected internal virtual void OnRemoveColumn (DataColumn column) {
\r
1759 /// Raises the RowChanged event.
\r
1761 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
\r
1762 if (null != RowChanged) {
\r
1763 RowChanged(this, e);
\r
1769 /// Raises the RowChanging event.
\r
1771 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
\r
1772 if (null != RowChanging) {
\r
1773 RowChanging(this, e);
\r
1778 /// Raises the RowDeleted event.
\r
1780 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
\r
1781 if (null != RowDeleted) {
\r
1782 RowDeleted(this, e);
\r
1787 /// Raises the RowDeleting event.
\r
1789 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
\r
1790 if (null != RowDeleting) {
\r
1791 RowDeleting(this, e);
\r
1796 /// Occurs when after a value has been changed for
\r
1797 /// the specified DataColumn in a DataRow.
\r
1799 [DataCategory ("Data")]
\r
1800 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1801 public event DataColumnChangeEventHandler ColumnChanged;
\r
1804 /// Occurs when a value is being changed for the specified
\r
1805 /// DataColumn in a DataRow.
\r
1807 [DataCategory ("Data")]
\r
1808 [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
1809 public event DataColumnChangeEventHandler ColumnChanging;
\r
1812 /// Occurs after a DataRow has been changed successfully.
\r
1814 [DataCategory ("Data")]
\r
1815 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1816 public event DataRowChangeEventHandler RowChanged;
\r
1819 /// Occurs when a DataRow is changing.
\r
1821 [DataCategory ("Data")]
\r
1822 [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
1823 public event DataRowChangeEventHandler RowChanging;
\r
1826 /// Occurs after a row in the table has been deleted.
\r
1828 [DataCategory ("Data")]
\r
1829 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1830 public event DataRowChangeEventHandler RowDeleted;
\r
1833 /// Occurs before a row in the table is about to be deleted.
\r
1835 [DataCategory ("Data")]
\r
1836 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1837 public event DataRowChangeEventHandler RowDeleting;
\r
1841 /// Occurs after the Clear method is called on the datatable.
\r
1843 [DataCategory ("Data")]
\r
1844 [DataSysDescription ("Occurs when the rows in a table is cleared . Throw an exception to cancel the deletion.")]
\r
1845 public event DataTableClearEventHandler TableCleared;
\r
1848 #endregion // Events
\r
1851 /// Removes all UniqueConstraints
\r
1853 private void RemoveUniqueConstraints ()
\r
1855 foreach (Constraint Cons in Constraints) {
\r
1857 if (Cons is UniqueConstraint) {
\r
1858 Constraints.Remove (Cons);
\r
1863 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
\r
1866 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)
\r
1868 DataColumn[] sortColumns = _emptyColumnArray;
\r
1869 sortDirections = null;
\r
1871 ArrayList columns = null;
\r
1872 ArrayList sorts = null;
\r
1874 if (sort != null && !sort.Equals ("")) {
\r
1875 columns = new ArrayList ();
\r
1876 sorts = new ArrayList();
\r
1877 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1879 for (int c = 0; c < columnExpression.Length; c++) {
\r
1880 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
1882 string columnName = columnSortInfo[0].Trim ();
\r
1883 string sortOrder = "ASC";
\r
1884 if (columnSortInfo.Length > 1)
\r
1885 sortOrder = columnSortInfo[1].Trim ().ToUpper (table.Locale);
\r
1887 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
1888 switch (sortOrder) {
\r
1890 sortDirection = ListSortDirection.Ascending;
\r
1893 sortDirection = ListSortDirection.Descending;
\r
1896 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
1899 if (columnName.StartsWith("[") || columnName.EndsWith("]")) {
\r
1900 if (columnName.StartsWith("[") && columnName.EndsWith("]"))
\r
1901 columnName = columnName.Substring(1, columnName.Length - 2);
\r
1903 throw new ArgumentException(String.Format("{0} isn't a valid Sort string entry.", columnName));
\r
1906 DataColumn dc = table.Columns[columnName];
\r
1909 dc = table.Columns[Int32.Parse (columnName)];
\r
1911 catch (FormatException) {
\r
1912 throw new IndexOutOfRangeException("Cannot find column " + columnName);
\r
1917 sorts.Add(sortDirection);
\r
1919 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));
\r
1920 sortDirections = new ListSortDirection[sorts.Count];
\r
1921 for (int i = 0; i < sortDirections.Length; i++)
\r
1922 sortDirections[i] = (ListSortDirection)sorts[i];
\r
1925 if (rejectNoResult) {
\r
1926 if (sortColumns == null)
\r
1927 throw new SystemException ("sort expression result is null");
\r
1928 if (sortColumns.Length == 0)
\r
1929 throw new SystemException("sort expression result is 0");
\r
1932 return sortColumns;
\r
1935 private void UpdatePropertyDescriptorsCache()
\r
1937 PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];
\r
1939 foreach(DataColumn col in Columns) {
\r
1940 descriptors[index++] = new DataColumnPropertyDescriptor(col);
\r
1943 foreach(DataRelation rel in ChildRelations) {
\r
1944 descriptors[index++] = new DataRelationPropertyDescriptor(rel);
\r
1947 _propertyDescriptorsCache = new PropertyDescriptorCollection(descriptors);
\r
1950 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()
\r
1952 if (_propertyDescriptorsCache == null) {
\r
1953 UpdatePropertyDescriptorsCache();
\r
1956 return _propertyDescriptorsCache;
\r
1959 internal void ResetPropertyDescriptorsCache() {
\r
1960 _propertyDescriptorsCache = null;
\r