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
47 using System.Collections.Generic;
\r
49 using System.ComponentModel;
\r
50 using System.Globalization;
\r
52 using System.Runtime.Serialization;
\r
54 using System.Xml.Schema;
\r
55 using System.Xml.Serialization;
\r
56 using System.Text.RegularExpressions;
\r
57 using Mono.Data.SqlExpressions;
\r
59 namespace System.Data {
\r
61 [ToolboxItem (false)]
\r
62 [DefaultEvent ("RowChanging")]
\r
63 [DefaultProperty ("TableName")]
\r
64 [DesignTimeVisible (false)]
\r
65 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
67 public partial class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable {
\r
70 internal DataSet dataSet;
\r
72 private bool _caseSensitive;
\r
73 private DataColumnCollection _columnCollection;
\r
74 private ConstraintCollection _constraintCollection;
\r
75 // never access it. Use DefaultView.
\r
76 private DataView _defaultView = null;
\r
78 private string _displayExpression;
\r
79 private PropertyCollection _extendedProperties;
\r
80 private CultureInfo _locale;
\r
81 private int _minimumCapacity;
\r
82 private string _nameSpace;
\r
83 private DataRelationCollection _childRelations;
\r
84 private DataRelationCollection _parentRelations;
\r
85 private string _prefix;
\r
86 private UniqueConstraint _primaryKeyConstraint;
\r
87 private DataRowCollection _rows;
\r
88 private ISite _site;
\r
89 private string _tableName;
\r
90 internal bool _duringDataLoad;
\r
91 internal bool _nullConstraintViolationDuringDataLoad;
\r
92 private bool dataSetPrevEnforceConstraints;
\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 // Regex to parse the Sort string.
\r
108 static Regex SortRegex = new Regex ( @"^((\[(?<ColName>.+)\])|(?<ColName>\S+))([ ]+(?<Order>ASC|DESC))?$",
\r
109 RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture);
\r
112 DataColumn [] _latestPrimaryKeyCols;
\r
113 #endregion //Fields
\r
116 /// Initializes a new instance of the DataTable class with no arguments.
\r
118 public DataTable ()
\r
121 _columnCollection = new DataColumnCollection(this);
\r
122 _constraintCollection = new ConstraintCollection(this);
\r
123 _extendedProperties = new PropertyCollection();
\r
126 _caseSensitive = false; //default value
\r
127 _displayExpression = null;
\r
128 _primaryKeyConstraint = null;
\r
130 _rows = new DataRowCollection (this);
\r
131 _indexes = new ArrayList();
\r
132 _recordCache = new RecordCache(this);
\r
134 //LAMESPEC: spec says 25 impl does 50
\r
135 _minimumCapacity = 50;
\r
137 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
138 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
\r
142 /// Intitalizes a new instance of the DataTable class with the specified table name.
\r
144 public DataTable (string tableName)
\r
147 _tableName = tableName;
\r
151 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
\r
153 protected DataTable (SerializationInfo info, StreamingContext context)
\r
157 SerializationInfoEnumerator e = info.GetEnumerator ();
\r
158 SerializationFormat serializationFormat = SerializationFormat.Xml;
\r
160 while (e.MoveNext()) {
\r
161 if (e.ObjectType == typeof(System.Data.SerializationFormat)) {
\r
162 serializationFormat = (SerializationFormat) e.Value;
\r
166 if (serializationFormat == SerializationFormat.Xml) {
\r
168 string schema = info.GetString ("XmlSchema");
\r
169 string data = info.GetString ("XmlDiffGram");
\r
171 DataSet ds = new DataSet ();
\r
172 ds.ReadXmlSchema (new StringReader (schema));
\r
173 ds.Tables [0].CopyProperties (this);
\r
174 ds = new DataSet ();
\r
175 ds.Tables.Add (this);
\r
176 ds.ReadXml (new StringReader (data), XmlReadMode.DiffGram);
\r
177 ds.Tables.Remove (this);
\r
178 /* keeping for a while. With the change above, we shouldn't have to consider
\r
179 * DataTable mode in schema inference/read.
\r
180 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
\r
181 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
\r
184 XmlDiffLoader loader = new XmlDiffLoader (this);
\r
185 xtr = new XmlTextReader(new StringReader (data));
\r
189 } else /*if (Tables.RemotingFormat == SerializationFormat.Binary)*/ {
\r
190 BinaryDeserializeTable (info);
\r
196 /// Indicates whether string comparisons within the table are case-sensitive.
\r
199 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
\r
201 public bool CaseSensitive {
\r
203 if (_virginCaseSensitive && dataSet != null)
\r
204 return dataSet.CaseSensitive;
\r
206 return _caseSensitive;
\r
209 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
210 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
212 _virginCaseSensitive = false;
\r
213 _caseSensitive = value;
\r
214 ResetCaseSensitiveIndexes();
\r
218 internal ArrayList Indexes {
\r
219 get { return _indexes; }
\r
222 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
\r
224 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
225 OnColumnChanged (e);
\r
228 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
\r
230 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
\r
231 OnColumnChanging (e);
\r
234 internal void DeletedDataRow (DataRow dr, DataRowAction action)
\r
236 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
240 internal void DeletingDataRow (DataRow dr, DataRowAction action)
\r
242 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
246 internal void ChangedDataRow (DataRow dr, DataRowAction action)
\r
248 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
252 internal void ChangingDataRow (DataRow dr, DataRowAction action)
\r
254 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
\r
259 /// Gets the collection of child relations for this DataTable.
\r
261 [Browsable (false)]
\r
263 [DataSysDescription ("Returns the child relations for this table.")]
\r
265 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
266 public DataRelationCollection ChildRelations {
\r
267 get { return _childRelations; }
\r
271 /// Gets the collection of columns that belong to this table.
\r
273 [DataCategory ("Data")]
\r
275 [DataSysDescription ("The collection that holds the columns for this table.")]
\r
277 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
278 public DataColumnCollection Columns {
\r
279 get { return _columnCollection; }
\r
283 /// Gets the collection of constraints maintained by this table.
\r
285 [DataCategory ("Data")]
\r
287 [DataSysDescription ("The collection that holds the constraints for this table.")]
\r
289 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
\r
290 public ConstraintCollection Constraints {
\r
291 get { return _constraintCollection; }
\r
293 internal set { _constraintCollection = value; }
\r
298 /// Gets the DataSet that this table belongs to.
\r
300 [Browsable (false)]
\r
302 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
\r
304 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
305 public DataSet DataSet {
\r
306 get { return dataSet; }
\r
310 /// Gets a customized view of the table which may
\r
311 /// include a filtered view, or a cursor position.
\r
313 [Browsable (false)]
\r
315 [DataSysDescription ("This is the default DataView for the table.")]
\r
317 public DataView DefaultView {
\r
319 if (_defaultView == null) {
\r
321 if (_defaultView == null){
\r
322 if (dataSet != null)
\r
323 _defaultView = dataSet.DefaultViewManager.CreateDataView(this);
\r
325 _defaultView = new DataView(this);
\r
329 return _defaultView;
\r
335 /// Gets or sets the expression that will return
\r
336 /// a value used to represent this table in the user interface.
\r
338 [DataCategory ("Data")]
\r
340 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
\r
342 [DefaultValue ("")]
\r
343 public string DisplayExpression {
\r
344 get { return _displayExpression == null ? "" : _displayExpression; }
\r
345 set { _displayExpression = value; }
\r
349 /// Gets the collection of customized user information.
\r
351 [Browsable (false)]
\r
352 [DataCategory ("Data")]
\r
354 [DataSysDescription ("The collection that holds custom user information.")]
\r
356 public PropertyCollection ExtendedProperties {
\r
357 get { return _extendedProperties; }
\r
361 /// Gets a value indicating whether there are errors in
\r
362 /// any of the_rows in any of the tables of the DataSet to
\r
363 /// which the table belongs.
\r
365 [Browsable (false)]
\r
367 [DataSysDescription ("Returns whether the table has errors.")]
\r
369 public bool HasErrors {
\r
371 // we can not use the _hasError flag because we do not know when to turn it off!
\r
372 for (int i = 0; i < _rows.Count; i++) {
\r
373 if (_rows[i].HasErrors)
\r
381 /// Gets or sets the locale information used to
\r
382 /// compare strings within the table.
\r
385 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
\r
387 public CultureInfo Locale {
\r
389 // if the locale is null, we check for the DataSet locale
\r
390 // and if the DataSet is null we return the current culture.
\r
391 // this way if DataSet locale is changed, only if there is no locale for
\r
392 // the DataTable it influece the Locale get;
\r
393 if (_locale != null)
\r
395 if (DataSet != null)
\r
396 return DataSet.Locale;
\r
397 return CultureInfo.CurrentCulture;
\r
400 if (_childRelations.Count > 0 || _parentRelations.Count > 0) {
\r
401 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
403 if (_locale == null || !_locale.Equals(value))
\r
408 internal bool LocaleSpecified {
\r
409 get { return _locale != null; }
\r
413 /// Gets or sets the initial starting size for this table.
\r
415 [DataCategory ("Data")]
\r
417 [DataSysDescription ("Indicates an initial starting size for this table.")]
\r
419 [DefaultValue (50)]
\r
420 public int MinimumCapacity {
\r
421 get { return _minimumCapacity; }
\r
422 set { _minimumCapacity = value; }
\r
426 /// Gets or sets the namespace for the XML represenation
\r
427 /// of the data stored in the DataTable.
\r
429 [DataCategory ("Data")]
\r
431 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
\r
433 public string Namespace {
\r
435 if (_nameSpace != null)
\r
437 if (DataSet != null)
\r
438 return DataSet.Namespace;
\r
439 return String.Empty;
\r
441 set { _nameSpace = value; }
\r
445 /// Gets the collection of parent relations for
\r
446 /// this DataTable.
\r
448 [Browsable (false)]
\r
450 [DataSysDescription ("Returns the parent relations for this table.")]
\r
452 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
453 public DataRelationCollection ParentRelations {
\r
454 get { return _parentRelations; }
\r
458 /// Gets or sets the namespace for the XML represenation
\r
459 /// of the data stored in the DataTable.
\r
461 [DataCategory ("Data")]
\r
463 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
\r
465 [DefaultValue ("")]
\r
466 public string Prefix {
\r
467 get { return _prefix == null ? "" : _prefix; }
\r
469 // Prefix cannot contain any special characters other than '_' and ':'
\r
470 for (int i = 0; i < value.Length; i++) {
\r
471 if (!(Char.IsLetterOrDigit (value [i])) && (value [i] != '_') && (value [i] != ':'))
\r
472 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
\r
479 /// Gets or sets an array of columns that function as
\r
480 /// primary keys for the data table.
\r
482 [DataCategory ("Data")]
\r
484 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
\r
486 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
\r
487 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, " + Consts.AssemblySystem_Data)]
\r
488 public DataColumn[] PrimaryKey {
\r
490 if (_primaryKeyConstraint == null)
\r
491 return new DataColumn[] {};
\r
492 return _primaryKeyConstraint.Columns;
\r
495 if (value == null || value.Length == 0) {
\r
496 if (_primaryKeyConstraint != null) {
\r
497 _primaryKeyConstraint.SetIsPrimaryKey (false);
\r
498 Constraints.Remove(_primaryKeyConstraint);
\r
499 _primaryKeyConstraint = null;
\r
504 if (InitInProgress) {
\r
505 _latestPrimaryKeyCols = value;
\r
509 // first check if value is the same as current PK.
\r
510 if (_primaryKeyConstraint != null &&
\r
511 DataColumn.AreColumnSetsTheSame (value, _primaryKeyConstraint.Columns))
\r
514 //Does constraint exist for these columns
\r
515 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet (this.Constraints, (DataColumn[]) value);
\r
517 //if constraint doesn't exist for columns
\r
518 //create new unique primary key constraint
\r
520 foreach (DataColumn Col in (DataColumn []) value) {
\r
521 if (Col.Table == null)
\r
524 if (Columns.IndexOf (Col) < 0)
\r
525 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
\r
527 // create constraint with primary key indication set to false
\r
528 // to avoid recursion
\r
529 uc = new UniqueConstraint ((DataColumn []) value, false);
\r
530 Constraints.Add (uc);
\r
533 //Remove the existing primary key
\r
534 if (_primaryKeyConstraint != null) {
\r
535 _primaryKeyConstraint.SetIsPrimaryKey (false);
\r
536 Constraints.Remove (_primaryKeyConstraint);
\r
537 _primaryKeyConstraint = null;
\r
540 //set the constraint as the new primary key
\r
541 UniqueConstraint.SetAsPrimaryKey (Constraints, uc);
\r
542 _primaryKeyConstraint = uc;
\r
544 for (int j = 0; j < uc.Columns.Length; ++j)
\r
545 uc.Columns [j].AllowDBNull = false;
\r
549 internal UniqueConstraint PrimaryKeyConstraint {
\r
550 get { return _primaryKeyConstraint; }
\r
554 /// Gets the collection of_rows that belong to this table.
\r
556 [Browsable (false)]
\r
558 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
\r
560 public DataRowCollection Rows {
\r
561 get { return _rows; }
\r
565 /// Gets or sets an System.ComponentModel.ISite
\r
566 /// for the DataTable.
\r
568 [Browsable (false)]
\r
569 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
\r
570 public override ISite Site {
\r
571 get { return _site; }
\r
572 set { _site = value; }
\r
576 /// Gets or sets the name of the the DataTable.
\r
578 [DataCategory ("Data")]
\r
580 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
\r
582 [DefaultValue ("")]
\r
583 [RefreshProperties (RefreshProperties.All)]
\r
584 public string TableName {
\r
585 get { return _tableName == null ? "" : _tableName; }
\r
586 set { _tableName = value; }
\r
589 bool IListSource.ContainsListCollection {
\r
590 // the collection is a DataView
\r
591 get { return false; }
\r
594 internal RecordCache RecordCache {
\r
595 get { return _recordCache; }
\r
598 private DataRowBuilder RowBuilder {
\r
600 // initiate only one row builder.
\r
601 if (_rowBuilder == null)
\r
602 _rowBuilder = new DataRowBuilder (this, -1, 0);
\r
604 // new row get id -1.
\r
605 _rowBuilder._rowId = -1;
\r
607 return _rowBuilder;
\r
611 internal bool EnforceConstraints {
\r
612 get { return enforceConstraints; }
\r
614 if (value == enforceConstraints)
\r
618 // reset indexes since they may be outdated
\r
621 // assert all constraints
\r
622 foreach (Constraint constraint in Constraints)
\r
623 constraint.AssertConstraint ();
\r
625 AssertNotNullConstraints ();
\r
628 Constraint.ThrowConstraintException ();
\r
630 enforceConstraints = value;
\r
634 internal void AssertNotNullConstraints ()
\r
636 if (_duringDataLoad && !_nullConstraintViolationDuringDataLoad)
\r
640 for (int i = 0; i < Columns.Count; i++) {
\r
641 DataColumn column = Columns [i];
\r
642 if (column.AllowDBNull)
\r
644 for (int j = 0; j < Rows.Count; j++) {
\r
645 if (Rows [j].HasVersion (DataRowVersion.Default) && Rows[j].IsNull (column)) {
\r
647 string errMsg = String.Format ("Column '{0}' does not allow DBNull.Value.",
\r
648 column.ColumnName);
\r
649 Rows [j].SetColumnError (i, errMsg);
\r
650 Rows [j].RowError = errMsg;
\r
654 _nullConstraintViolationDuringDataLoad = seen;
\r
657 internal bool RowsExist (DataColumn [] columns, DataColumn [] relatedColumns, DataRow row)
\r
659 int curIndex = row.IndexFromVersion (DataRowVersion.Default);
\r
660 int tmpRecord = RecordCache.NewRecord ();
\r
663 for (int i = 0; i < relatedColumns.Length; i++)
\r
664 // according to MSDN: the DataType value for both columns must be identical.
\r
665 columns [i].DataContainer.CopyValue (relatedColumns [i].DataContainer, curIndex, tmpRecord);
\r
666 return RowsExist (columns, tmpRecord);
\r
668 RecordCache.DisposeRecord (tmpRecord);
\r
672 bool RowsExist (DataColumn [] columns, int index)
\r
674 Index indx = this.FindIndex (columns);
\r
677 return indx.Find (index) != -1;
\r
679 // we have to perform full-table scan
\r
680 // check that there is a parent for this row.
\r
681 foreach (DataRow thisRow in this.Rows) {
\r
682 if (thisRow.RowState == DataRowState.Deleted)
\r
684 // check if the values in the columns are equal
\r
685 int thisIndex = thisRow.IndexFromVersion (
\r
686 thisRow.RowState == DataRowState.Modified ? DataRowVersion.Original : DataRowVersion.Current);
\r
688 foreach (DataColumn column in columns) {
\r
689 if (column.DataContainer.CompareValues (thisIndex, index) != 0) {
\r
701 /// Commits all the changes made to this table since the
\r
702 /// last time AcceptChanges was called.
\r
704 public void AcceptChanges ()
\r
706 //FIXME: Do we need to validate anything here or
\r
707 //try to catch any errors to deal with them?
\r
709 // we do not use foreach because if one of the rows is in Delete state
\r
710 // it will be romeved from Rows and we get an exception.
\r
712 for (int i = 0; i < Rows.Count; ) {
\r
714 myRow.AcceptChanges ();
\r
716 // if the row state is Detached it meens that it was removed from row list (Rows)
\r
717 // so we should not increase 'i'.
\r
718 if (myRow.RowState != DataRowState.Detached)
\r
721 _rows.OnListChanged (this, new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
725 /// Begins the initialization of a DataTable that is used
\r
726 /// on a form or used by another component. The initialization
\r
727 /// occurs at runtime.
\r
735 InitInProgress = true;
\r
737 tableInitialized = false;
\r
742 /// Turns off notifications, index maintenance, and
\r
743 /// constraints while loading data.
\r
745 public void BeginLoadData ()
\r
747 if (this._duringDataLoad)
\r
750 //duringDataLoad is important to EndLoadData and
\r
751 //for not throwing unexpected exceptions.
\r
752 this._duringDataLoad = true;
\r
753 this._nullConstraintViolationDuringDataLoad = false;
\r
755 if (this.dataSet != null) {
\r
756 //Saving old Enforce constraints state for later
\r
757 //use in the EndLoadData.
\r
758 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
\r
759 this.dataSet.EnforceConstraints = false;
\r
761 //if table does not belong to any data set use EnforceConstraints of the table
\r
762 this.EnforceConstraints = false;
\r
768 /// Clears the DataTable of all data.
\r
770 public void Clear ()
\r
772 // Foriegn key constraints are checked in _rows.Clear method
\r
777 /// Clones the structure of the DataTable, including
\r
778 /// all DataTable schemas and constraints.
\r
780 public virtual DataTable Clone ()
\r
782 // Use Activator so we can use non-public constructors.
\r
783 DataTable Copy = (DataTable) Activator.CreateInstance (GetType (), true);
\r
784 CopyProperties (Copy);
\r
789 /// Computes the given expression on the current_rows that
\r
790 /// pass the filter criteria.
\r
792 public object Compute (string expression, string filter)
\r
794 // expression is an aggregate function
\r
795 // filter is an expression used to limit rows
\r
797 DataRow [] rows = Select (filter);
\r
799 if (rows == null || rows.Length == 0)
\r
800 return DBNull.Value;
\r
802 Parser parser = new Parser (rows);
\r
803 IExpression expr = parser.Compile (expression);
\r
804 object obj = expr.Eval (rows [0]);
\r
810 /// Copies both the structure and data for this DataTable.
\r
812 public DataTable Copy ()
\r
814 DataTable copy = Clone ();
\r
816 copy._duringDataLoad = true;
\r
817 foreach (DataRow row in Rows) {
\r
818 DataRow newRow = copy.NewNotInitializedRow ();
\r
819 copy.Rows.AddInternal (newRow);
\r
820 CopyRow (row, newRow);
\r
822 copy._duringDataLoad = false;
\r
824 // rebuild copy indexes after loading all rows
\r
825 copy.ResetIndexes ();
\r
829 internal void CopyRow (DataRow fromRow, DataRow toRow)
\r
831 if (fromRow.HasErrors)
\r
832 fromRow.CopyErrors (toRow);
\r
834 if (fromRow.HasVersion (DataRowVersion.Original))
\r
835 toRow.Original = toRow.Table.RecordCache.CopyRecord (this, fromRow.Original, -1);
\r
837 if (fromRow.HasVersion (DataRowVersion.Current)) {
\r
838 if (fromRow.Original != fromRow.Current)
\r
839 toRow.Current = toRow.Table.RecordCache.CopyRecord (this, fromRow.Current, -1);
\r
841 toRow.Current = toRow.Original;
\r
845 private void CopyProperties (DataTable Copy)
\r
847 Copy.CaseSensitive = CaseSensitive;
\r
848 Copy._virginCaseSensitive = _virginCaseSensitive;
\r
850 // Copy.ChildRelations
\r
851 // Copy.Constraints
\r
853 // Copy.DefaultView
\r
855 Copy.DisplayExpression = DisplayExpression;
\r
856 if (ExtendedProperties.Count > 0) {
\r
857 // Cannot copy extended properties directly as the property does not have a set accessor
\r
858 Array tgtArray = Array.CreateInstance (typeof (object), ExtendedProperties.Count);
\r
859 ExtendedProperties.Keys.CopyTo (tgtArray, 0);
\r
860 for (int i=0; i < ExtendedProperties.Count; i++)
\r
861 Copy.ExtendedProperties.Add (tgtArray.GetValue (i), ExtendedProperties[tgtArray.GetValue (i)]);
\r
863 Copy._locale = _locale;
\r
864 Copy.MinimumCapacity = MinimumCapacity;
\r
865 Copy.Namespace = Namespace;
\r
866 // Copy.ParentRelations
\r
867 Copy.Prefix = Prefix;
\r
869 Copy.TableName = TableName;
\r
871 bool isEmpty = Copy.Columns.Count == 0;
\r
874 foreach (DataColumn column in Columns) {
\r
875 // When cloning a table, the columns may be added in the default constructor.
\r
876 if (isEmpty || !Copy.Columns.Contains (column.ColumnName))
\r
877 Copy.Columns.Add (column.Clone ());
\r
879 foreach (DataColumn column in Copy.Columns)
\r
880 column.CompileExpression ();
\r
882 CopyConstraints (Copy);
\r
883 // add primary key to the copy
\r
884 if (PrimaryKey.Length > 0) {
\r
885 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
\r
886 for (int i = 0; i < pColumns.Length; i++)
\r
887 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
\r
889 Copy.PrimaryKey = pColumns;
\r
893 private void CopyConstraints (DataTable copy)
\r
895 UniqueConstraint origUc;
\r
896 UniqueConstraint copyUc;
\r
897 for (int i = 0; i < this.Constraints.Count; i++) {
\r
898 if (this.Constraints[i] is UniqueConstraint) {
\r
899 // typed ds can already contain the constraints
\r
900 if (copy.Constraints.Contains (this.Constraints [i].ConstraintName))
\r
903 origUc = (UniqueConstraint) this.Constraints [i];
\r
904 DataColumn [] columns = new DataColumn [origUc.Columns.Length];
\r
905 for (int j = 0; j < columns.Length; j++)
\r
906 columns[j] = copy.Columns [origUc.Columns [j].ColumnName];
\r
908 copyUc = new UniqueConstraint (origUc.ConstraintName, columns, origUc.IsPrimaryKey);
\r
909 copy.Constraints.Add (copyUc);
\r
914 /// Ends the initialization of a DataTable that is used
\r
915 /// on a form or used by another component. The
\r
916 /// initialization occurs at runtime.
\r
924 InitInProgress = false;
\r
925 DataTableInitialized ();
\r
929 // defined in NET_2_0 profile
\r
930 partial void DataTableInitialized ();
\r
932 internal bool InitInProgress {
\r
933 get { return fInitInProgress; }
\r
934 set { fInitInProgress = value; }
\r
937 internal void FinishInit ()
\r
939 UniqueConstraint oldPK = _primaryKeyConstraint;
\r
941 // Columns shud be added 'before' the constraints
\r
942 Columns.PostAddRange ();
\r
944 // Add the constraints
\r
945 _constraintCollection.PostAddRange ();
\r
947 // ms.net behavior : If a PrimaryKey (UniqueConstraint) is added thru AddRange,
\r
948 // then it takes precedence over an direct assignment of PrimaryKey
\r
949 if (_primaryKeyConstraint == oldPK)
\r
950 PrimaryKey = _latestPrimaryKeyCols;
\r
954 /// Turns on notifications, index maintenance, and
\r
955 /// constraints after loading data.
\r
957 public void EndLoadData ()
\r
959 if (this._duringDataLoad) {
\r
960 //Getting back to previous EnforceConstraint state
\r
961 if (this.dataSet != null)
\r
962 this.dataSet.InternalEnforceConstraints (this.dataSetPrevEnforceConstraints, true);
\r
964 this.EnforceConstraints = true;
\r
966 this._duringDataLoad = false;
\r
971 /// Gets a copy of the DataTable that contains all
\r
972 /// changes made to it since it was loaded or
\r
973 /// AcceptChanges was last called.
\r
975 public DataTable GetChanges ()
\r
977 return GetChanges (DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
\r
981 /// Gets a copy of the DataTable containing all
\r
982 /// changes made to it since it was last loaded, or
\r
983 /// since AcceptChanges was called, filtered by DataRowState.
\r
985 public DataTable GetChanges (DataRowState rowStates)
\r
987 DataTable copyTable = null;
\r
989 foreach (DataRow row in Rows) {
\r
990 // The spec says relationship constraints may cause Unchanged parent rows to be included but
\r
991 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
\r
992 if (!row.IsRowChanged (rowStates))
\r
994 if (copyTable == null)
\r
995 copyTable = Clone ();
\r
996 DataRow newRow = copyTable.NewNotInitializedRow ();
\r
997 // Don't check for ReadOnly, when cloning data to new uninitialized row.
\r
998 row.CopyValuesToRow (newRow, false);
\r
1000 newRow.XmlRowID = row.XmlRowID;
\r
1002 copyTable.Rows.AddInternal (newRow);
\r
1009 /// Gets an array of DataRow objects that contain errors.
\r
1011 public DataRow [] GetErrors ()
\r
1013 ArrayList errors = new ArrayList();
\r
1014 for (int i = 0; i < _rows.Count; i++) {
\r
1015 if (_rows[i].HasErrors)
\r
1016 errors.Add (_rows[i]);
\r
1019 DataRow[] ret = NewRowArray (errors.Count);
\r
1020 errors.CopyTo (ret, 0);
\r
1025 /// This member is only meant to support Mono's infrastructure
\r
1027 protected virtual DataTable CreateInstance ()
\r
1029 return Activator.CreateInstance (this.GetType (), true) as DataTable;
\r
1033 /// This member is only meant to support Mono's infrastructure
\r
1035 protected virtual Type GetRowType ()
\r
1037 return typeof (DataRow);
\r
1041 /// This member is only meant to support Mono's infrastructure
\r
1043 /// Used for Data Binding between System.Web.UI. controls
\r
1044 /// like a DataGrid
\r
1046 /// System.Windows.Forms controls like a DataGrid
\r
1048 IList IListSource.GetList ()
\r
1050 IList list = (IList) DefaultView;
\r
1055 /// Copies a DataRow into a DataTable, preserving any
\r
1056 /// property settings, as well as original and current values.
\r
1058 public void ImportRow (DataRow row)
\r
1060 if (row.RowState == DataRowState.Detached)
\r
1063 DataRow newRow = NewNotInitializedRow ();
\r
1065 int original = -1;
\r
1066 if (row.HasVersion (DataRowVersion.Original)) {
\r
1067 original = row.IndexFromVersion (DataRowVersion.Original);
\r
1068 newRow.Original = RecordCache.NewRecord ();
\r
1069 RecordCache.CopyRecord (row.Table, original, newRow.Original);
\r
1072 if (row.HasVersion (DataRowVersion.Current)) {
\r
1073 int current = row.IndexFromVersion (DataRowVersion.Current);
\r
1074 if (current == original) {
\r
1075 newRow.Current = newRow.Original;
\r
1077 newRow.Current = RecordCache.NewRecord ();
\r
1078 RecordCache.CopyRecord (row.Table, current, newRow.Current);
\r
1082 //Import the row only if RowState is not detached
\r
1083 //Validation for Deleted Rows happens during Accept/RejectChanges
\r
1084 if (row.RowState != DataRowState.Deleted)
\r
1085 newRow.Validate ();
\r
1087 AddRowToIndexes (newRow);
\r
1088 Rows.AddInternal(newRow);
\r
1090 if (row.HasErrors)
\r
1091 row.CopyErrors (newRow);
\r
1094 internal int DefaultValuesRowIndex {
\r
1095 get { return _defaultValuesRowIndex; }
\r
1099 /// This member is only meant to support Mono's infrastructure
\r
1108 GetObjectData (SerializationInfo info, StreamingContext context)
\r
1111 if (RemotingFormat == SerializationFormat.Xml) {
\r
1114 if (dataSet != null)
\r
1117 dset = new DataSet ("tmpDataSet");
\r
1118 dset.Tables.Add (this);
\r
1121 StringWriter sw = new StringWriter ();
\r
1122 XmlTextWriter tw = new XmlTextWriter (sw);
\r
1123 tw.Formatting = Formatting.Indented;
\r
1124 dset.WriteIndividualTableContent (tw, this, XmlWriteMode.DiffGram);
\r
1127 StringWriter sw2 = new StringWriter ();
\r
1128 DataTableCollection tables = new DataTableCollection (dset);
\r
1129 tables.Add (this);
\r
1130 XmlSchemaWriter.WriteXmlSchema (dset, new XmlTextWriter (sw2), tables, null);
\r
1133 info.AddValue ("XmlSchema", sw2.ToString(), typeof(string));
\r
1134 info.AddValue ("XmlDiffGram", sw.ToString(), typeof(string));
\r
1136 } else /*if (RemotingFormat == SerializationFormat.Binary)*/ {
\r
1137 BinarySerializeProperty (info);
\r
1138 if (dataSet == null) {
\r
1139 for (int i = 0; i < Columns.Count; i++) {
\r
1140 info.AddValue ("DataTable.DataColumn_" + i + ".Expression",
\r
1141 Columns[i].Expression);
\r
1143 BinarySerialize (info, "DataTable_0.");
\r
1150 /// Finds and updates a specific row. If no matching row
\r
1151 /// is found, a new row is created using the given values.
\r
1153 public DataRow LoadDataRow (object [] values, bool fAcceptChanges)
\r
1155 DataRow row = null;
\r
1156 if (PrimaryKey.Length == 0) {
\r
1157 row = Rows.Add (values);
\r
1159 EnsureDefaultValueRowIndex ();
\r
1160 int newRecord = CreateRecord (values);
\r
1161 int existingRecord = _primaryKeyConstraint.Index.Find (newRecord);
\r
1163 if (existingRecord < 0) {
\r
1164 row = NewRowFromBuilder (RowBuilder);
\r
1165 row.Proposed = newRecord;
\r
1166 Rows.AddInternal(row);
\r
1167 if (!_duringDataLoad)
\r
1168 AddRowToIndexes (row);
\r
1170 row = RecordCache [existingRecord];
\r
1172 row.ImportRecord (newRecord);
\r
1177 if (fAcceptChanges)
\r
1178 row.AcceptChanges ();
\r
1183 internal DataRow LoadDataRow (IDataRecord record, int[] mapping, int length, bool fAcceptChanges)
\r
1185 DataRow row = null;
\r
1186 int tmpRecord = this.RecordCache.NewRecord ();
\r
1188 RecordCache.ReadIDataRecord (tmpRecord,record,mapping,length);
\r
1189 if (PrimaryKey.Length != 0) {
\r
1190 bool hasPrimaryValues = true;
\r
1191 foreach(DataColumn col in PrimaryKey) {
\r
1192 if(!(col.Ordinal < mapping.Length)) {
\r
1193 hasPrimaryValues = false;
\r
1198 if (hasPrimaryValues) {
\r
1199 int existingRecord = _primaryKeyConstraint.Index.Find (tmpRecord);
\r
1200 if (existingRecord != -1)
\r
1201 row = RecordCache [existingRecord];
\r
1205 if (row == null) {
\r
1206 row = NewNotInitializedRow ();
\r
1207 row.Proposed = tmpRecord;
\r
1208 Rows.AddInternal (row);
\r
1211 row.ImportRecord (tmpRecord);
\r
1215 if (fAcceptChanges)
\r
1216 row.AcceptChanges ();
\r
1219 this.RecordCache.DisposeRecord (tmpRecord);
\r
1226 /// Creates a new DataRow with the same schema as the table.
\r
1228 public DataRow NewRow ()
\r
1230 EnsureDefaultValueRowIndex();
\r
1232 DataRow newRow = NewRowFromBuilder (RowBuilder);
\r
1233 newRow.Proposed = CreateRecord (null);
\r
1234 NewRowAdded (newRow);
\r
1238 // defined in the NET_2_0 profile
\r
1239 partial void NewRowAdded (DataRow dr);
\r
1241 internal int CreateRecord (object [] values)
\r
1243 int valCount = values != null ? values.Length : 0;
\r
1244 if (valCount > Columns.Count)
\r
1245 throw new ArgumentException ("Input array is longer than the number of columns in this table.");
\r
1247 int index = RecordCache.NewRecord ();
\r
1250 for (int i = 0; i < valCount; i++) {
\r
1251 object value = values[i];
\r
1252 if (value == null)
\r
1253 Columns [i].SetDefaultValue (index);
\r
1255 Columns [i][index] = values [i];
\r
1258 for(int i = valCount; i < Columns.Count; i++)
\r
1259 Columns [i].SetDefaultValue (index);
\r
1263 RecordCache.DisposeRecord (index);
\r
1268 private void EnsureDefaultValueRowIndex ()
\r
1270 // initialize default values row for the first time
\r
1271 if (_defaultValuesRowIndex == -1) {
\r
1272 _defaultValuesRowIndex = RecordCache.NewRecord();
\r
1273 for (int i = 0; i < Columns.Count; ++i) {
\r
1274 DataColumn column = Columns [i];
\r
1275 column.DataContainer [_defaultValuesRowIndex] = column.DefaultValue;
\r
1281 /// This member supports the .NET Framework infrastructure
\r
1282 /// and is not intended to be used directly from your code.
\r
1284 DataRow [] empty_rows;
\r
1285 protected internal DataRow [] NewRowArray (int size)
\r
1287 if (size == 0 && empty_rows != null)
\r
1288 return empty_rows;
\r
1289 Type t = GetRowType ();
\r
1290 /* Avoid reflection if possible */
\r
1291 DataRow [] rows = t == typeof (DataRow) ? new DataRow [size] : (DataRow []) Array.CreateInstance (t, size);
\r
1293 empty_rows = rows;
\r
1298 /// Creates a new row from an existing row.
\r
1300 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
\r
1302 return new DataRow (builder);
\r
1305 internal DataRow NewNotInitializedRow ()
\r
1307 EnsureDefaultValueRowIndex ();
\r
1309 return NewRowFromBuilder (RowBuilder);
\r
1313 /// Rolls back all changes that have been made to the
\r
1314 /// table since it was loaded, or the last time AcceptChanges
\r
1317 public void RejectChanges ()
\r
1319 for (int i = _rows.Count - 1; i >= 0; i--) {
\r
1320 DataRow row = _rows [i];
\r
1321 if (row.RowState != DataRowState.Unchanged)
\r
1322 _rows [i].RejectChanges ();
\r
1327 /// Resets the DataTable to its original state.
\r
1329 public virtual void Reset ()
\r
1332 while (ParentRelations.Count > 0) {
\r
1333 if (dataSet.Relations.Contains (ParentRelations [ParentRelations.Count - 1].RelationName))
\r
1334 dataSet.Relations.Remove (ParentRelations [ParentRelations.Count - 1]);
\r
1337 while (ChildRelations.Count > 0) {
\r
1338 if (dataSet.Relations.Contains (ChildRelations [ChildRelations.Count - 1].RelationName))
\r
1339 dataSet.Relations.Remove (ChildRelations [ChildRelations.Count - 1]);
\r
1341 Constraints.Clear ();
\r
1346 /// Gets an array of all DataRow objects.
\r
1348 public DataRow[] Select ()
\r
1350 return Select (String.Empty, String.Empty, DataViewRowState.CurrentRows);
\r
1354 /// Gets an array of all DataRow objects that match
\r
1355 /// the filter criteria in order of primary key (or
\r
1356 /// lacking one, order of addition.)
\r
1358 public DataRow[] Select (string filterExpression)
\r
1360 return Select (filterExpression, String.Empty, DataViewRowState.CurrentRows);
\r
1364 /// Gets an array of all DataRow objects that
\r
1365 /// match the filter criteria, in the the
\r
1366 /// specified sort order.
\r
1368 public DataRow[] Select (string filterExpression, string sort)
\r
1370 return Select (filterExpression, sort, DataViewRowState.CurrentRows);
\r
1374 /// Gets an array of all DataRow objects that match
\r
1375 /// the filter in the order of the sort, that match
\r
1376 /// the specified state.
\r
1378 public DataRow [] Select (string filterExpression, string sort, DataViewRowState recordStates)
\r
1380 if (filterExpression == null)
\r
1381 filterExpression = String.Empty;
\r
1383 IExpression filter = null;
\r
1384 if (filterExpression != String.Empty) {
\r
1385 Parser parser = new Parser ();
\r
1386 filter = parser.Compile (filterExpression);
\r
1389 DataColumn [] columns = _emptyColumnArray;
\r
1390 ListSortDirection [] sorts = null;
\r
1392 if (sort != null && !sort.Equals(String.Empty))
\r
1393 columns = ParseSortString (this, sort, out sorts, false);
\r
1395 if (Rows.Count == 0)
\r
1396 return NewRowArray (0);
\r
1398 //if sort order is not given, sort it in Ascending order of the
\r
1399 //columns involved in the filter
\r
1400 if (columns.Length == 0 && filter != null) {
\r
1401 ArrayList list = new ArrayList ();
\r
1402 for (int i = 0; i < Columns.Count; ++i) {
\r
1403 if (!filter.DependsOn (Columns [i]))
\r
1405 list.Add (Columns [i]);
\r
1407 columns = (DataColumn []) list.ToArray (typeof (DataColumn));
\r
1410 bool addIndex = true;
\r
1411 if (filterExpression != String.Empty)
\r
1413 Index index = GetIndex (columns, sorts, recordStates, filter, false, addIndex);
\r
1415 int [] records = index.GetAll ();
\r
1416 DataRow [] dataRows = NewRowArray (index.Size);
\r
1417 for (int i = 0; i < dataRows.Length; i++)
\r
1418 dataRows [i] = RecordCache [records [i]];
\r
1423 private void AddIndex (Index index)
\r
1425 if (_indexes == null)
\r
1426 _indexes = new ArrayList();
\r
1427 _indexes.Add (index);
\r
1431 /// Returns index corresponding to columns,sort,row state filter and unique values given.
\r
1432 /// If such an index not exists, creates a new one.
\r
1434 /// <param name="columns">Columns set of the index to look for.</param>
\r
1435 /// <param name="sort">Columns sort order of the index to look for.</param>
\r
1436 /// <param name="rowState">Rpw state filter of the index to look for.</param>
\r
1437 /// <param name="unique">Uniqueness of the index to look for.</param>
\r
1438 /// <param name="strict">Indicates whenever the index found should correspond in its uniquness to the value of unique parameter specified.</param>
\r
1439 /// <param name="reset">Indicates whenever the already existing index should be forced to reset.</param>
\r
1440 /// <returns></returns>
\r
1441 internal Index GetIndex (DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter, bool reset)
\r
1443 return GetIndex (columns, sort, rowState, filter, reset, true);
\r
1446 internal Index GetIndex (DataColumn[] columns, ListSortDirection[] sort,
\r
1447 DataViewRowState rowState, IExpression filter,
\r
1448 bool reset, bool addIndex)
\r
1450 Index index = FindIndex(columns, sort, rowState, filter);
\r
1451 if (index == null) {
\r
1452 index = new Index(new Key (this, columns, sort, rowState, filter));
\r
1456 } else if (reset) {
\r
1457 // reset existing index only if asked for this
\r
1463 internal Index FindIndex (DataColumn[] columns)
\r
1465 return FindIndex (columns, null, DataViewRowState.None, null);
\r
1468 internal Index FindIndex (DataColumn[] columns, ListSortDirection[] sort, DataViewRowState rowState, IExpression filter)
\r
1470 if (Indexes != null) {
\r
1471 foreach (Index index in Indexes) {
\r
1472 if (index.Key.Equals (columns,sort,rowState, filter))
\r
1479 internal void ResetIndexes ()
\r
1481 foreach(Index index in Indexes)
\r
1485 internal void ResetCaseSensitiveIndexes ()
\r
1487 foreach (Index index in Indexes) {
\r
1488 bool containsStringcolumns = false;
\r
1489 foreach(DataColumn column in index.Key.Columns) {
\r
1490 if (column.DataType == typeof(string)) {
\r
1491 containsStringcolumns = true;
\r
1496 if (!containsStringcolumns && index.Key.HasFilter) {
\r
1497 foreach (DataColumn column in Columns) {
\r
1498 if ((column.DataType == DbTypes.TypeOfString) && (index.Key.DependsOn (column))) {
\r
1499 containsStringcolumns = true;
\r
1505 if (containsStringcolumns)
\r
1510 internal void DropIndex (Index index)
\r
1512 if (index != null && index.RefCount == 0) {
\r
1513 _indexes.Remove (index);
\r
1517 internal void DropReferencedIndexes (DataColumn column)
\r
1519 if (_indexes != null)
\r
1520 for (int i = _indexes.Count - 1; i >= 0; i--) {
\r
1521 Index indx = (Index)_indexes [i];
\r
1522 if (indx.Key.DependsOn (column))
\r
1523 _indexes.Remove (indx);
\r
1527 internal void AddRowToIndexes (DataRow row)
\r
1529 if (_indexes != null) {
\r
1530 for (int i = 0; i < _indexes.Count; ++i)
\r
1531 ((Index)_indexes [i]).Add (row);
\r
1535 internal void DeleteRowFromIndexes (DataRow row)
\r
1537 if (_indexes != null) {
\r
1538 foreach (Index indx in _indexes)
\r
1539 indx.Delete (row);
\r
1544 /// Gets the TableName and DisplayExpression, if
\r
1545 /// there is one as a concatenated string.
\r
1547 public override string ToString ()
\r
1549 //LAMESPEC: spec says concat the two. impl puts a
\r
1550 //plus sign infront of DisplayExpression
\r
1551 string retVal = TableName;
\r
1552 if(DisplayExpression != null && DisplayExpression != "")
\r
1553 retVal += " + " + DisplayExpression;
\r
1560 /// Raises the ColumnChanged event.
\r
1562 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e)
\r
1564 if (null != ColumnChanged)
\r
1565 ColumnChanged (this, e);
\r
1568 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e)
\r
1570 OnColumnChanged (e);
\r
1574 /// Raises the ColumnChanging event.
\r
1576 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e)
\r
1578 if (null != ColumnChanging)
\r
1579 ColumnChanging (this, e);
\r
1582 internal void RaiseOnColumnChanging (DataColumnChangeEventArgs e)
\r
1584 OnColumnChanging(e);
\r
1588 /// Raises the PropertyChanging event.
\r
1591 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent)
\r
1593 //if (null != PropertyChanging)
\r
1595 // PropertyChanging (this, pcevent);
\r
1597 throw new NotImplementedException ();
\r
1601 /// Notifies the DataTable that a DataColumn is being removed.
\r
1603 protected internal virtual void OnRemoveColumn (DataColumn column)
\r
1605 DropReferencedIndexes (column);
\r
1609 /// Raises the RowChanged event.
\r
1611 protected virtual void OnRowChanged (DataRowChangeEventArgs e)
\r
1613 if (null != RowChanged)
\r
1614 RowChanged (this, e);
\r
1619 /// Raises the RowChanging event.
\r
1621 protected virtual void OnRowChanging (DataRowChangeEventArgs e)
\r
1623 if (null != RowChanging)
\r
1624 RowChanging (this, e);
\r
1628 /// Raises the RowDeleted event.
\r
1630 protected virtual void OnRowDeleted (DataRowChangeEventArgs e)
\r
1632 if (null != RowDeleted)
\r
1633 RowDeleted (this, e);
\r
1637 /// Raises the RowDeleting event.
\r
1639 protected virtual void OnRowDeleting (DataRowChangeEventArgs e)
\r
1641 if (null != RowDeleting)
\r
1642 RowDeleting (this, e);
\r
1646 /// Occurs when after a value has been changed for
\r
1647 /// the specified DataColumn in a DataRow.
\r
1649 [DataCategory ("Data")]
\r
1651 [DataSysDescription ("Occurs when a value has been changed for this column.")]
\r
1653 public event DataColumnChangeEventHandler ColumnChanged;
\r
1656 /// Occurs when a value is being changed for the specified
\r
1657 /// DataColumn in a DataRow.
\r
1659 [DataCategory ("Data")]
\r
1661 [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
1663 public event DataColumnChangeEventHandler ColumnChanging;
\r
1666 /// Occurs after a DataRow has been changed successfully.
\r
1668 [DataCategory ("Data")]
\r
1670 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
\r
1672 public event DataRowChangeEventHandler RowChanged;
\r
1675 /// Occurs when a DataRow is changing.
\r
1677 [DataCategory ("Data")]
\r
1679 [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
1681 public event DataRowChangeEventHandler RowChanging;
\r
1684 /// Occurs after a row in the table has been deleted.
\r
1686 [DataCategory ("Data")]
\r
1688 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
\r
1690 public event DataRowChangeEventHandler RowDeleted;
\r
1693 /// Occurs before a row in the table is about to be deleted.
\r
1695 [DataCategory ("Data")]
\r
1697 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
\r
1699 public event DataRowChangeEventHandler RowDeleting;
\r
1701 #endregion // Events
\r
1703 internal static DataColumn[] ParseSortString (DataTable table, string sort, out ListSortDirection[] sortDirections, bool rejectNoResult)
\r
1705 DataColumn[] sortColumns = _emptyColumnArray;
\r
1706 sortDirections = null;
\r
1708 ArrayList columns = null;
\r
1709 ArrayList sorts = null;
\r
1711 if (sort != null && !sort.Equals ("")) {
\r
1712 columns = new ArrayList ();
\r
1713 sorts = new ArrayList();
\r
1714 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
1716 for (int c = 0; c < columnExpression.Length; c++) {
\r
1717 string rawColumnName = columnExpression[c].Trim ();
\r
1719 Match match = SortRegex.Match (rawColumnName);
\r
1720 Group g = match.Groups["ColName"] ;
\r
1722 throw new IndexOutOfRangeException ("Could not find column: " + rawColumnName);
\r
1724 string columnName = g.Value;
\r
1725 DataColumn dc = table.Columns[columnName];
\r
1728 dc = table.Columns[Int32.Parse (columnName)];
\r
1729 } catch (FormatException) {
\r
1730 throw new IndexOutOfRangeException("Cannot find column " + columnName);
\r
1735 g = match.Groups["Order"];
\r
1736 if (!g.Success || String.Compare (g.Value, "ASC", true, CultureInfo.InvariantCulture) == 0)
\r
1737 sorts.Add(ListSortDirection.Ascending);
\r
1739 sorts.Add (ListSortDirection.Descending);
\r
1742 sortColumns = (DataColumn[]) columns.ToArray (typeof (DataColumn));
\r
1743 sortDirections = new ListSortDirection[sorts.Count];
\r
1744 for (int i = 0; i < sortDirections.Length; i++)
\r
1745 sortDirections[i] = (ListSortDirection)sorts[i];
\r
1748 if (rejectNoResult) {
\r
1749 if (sortColumns == null)
\r
1750 throw new SystemException ("sort expression result is null");
\r
1751 if (sortColumns.Length == 0)
\r
1752 throw new SystemException("sort expression result is 0");
\r
1755 return sortColumns;
\r
1758 private void UpdatePropertyDescriptorsCache ()
\r
1760 PropertyDescriptor[] descriptors = new PropertyDescriptor[Columns.Count + ChildRelations.Count];
\r
1762 foreach (DataColumn col in Columns)
\r
1763 descriptors [index++] = new DataColumnPropertyDescriptor (col);
\r
1765 foreach (DataRelation rel in ChildRelations)
\r
1766 descriptors [index++] = new DataRelationPropertyDescriptor (rel);
\r
1768 _propertyDescriptorsCache = new PropertyDescriptorCollection (descriptors);
\r
1771 internal PropertyDescriptorCollection GetPropertyDescriptorCollection()
\r
1773 if (_propertyDescriptorsCache == null)
\r
1774 UpdatePropertyDescriptorsCache ();
\r
1775 return _propertyDescriptorsCache;
\r
1778 internal void ResetPropertyDescriptorsCache ()
\r
1780 _propertyDescriptorsCache = null;
\r
1783 internal void SetRowsID()
\r
1785 int dataRowID = 0;
\r
1786 foreach (DataRow row in Rows) {
\r
1787 row.XmlRowID = dataRowID;
\r
1794 [XmlSchemaProvider ("GetDataTableSchema")]
\r
1795 partial class DataTable : IXmlSerializable {
\r
1796 [MonoNotSupported ("")]
\r
1797 XmlSchema IXmlSerializable.GetSchema ()
\r
1799 return GetSchema ();
\r
1802 void IXmlSerializable.ReadXml (XmlReader reader)
\r
1804 ReadXml_internal (reader, true);
\r
1807 void IXmlSerializable.WriteXml (XmlWriter writer)
\r
1809 DataSet dset = dataSet;
\r
1810 bool isPartOfDataSet = true;
\r
1812 if (dataSet == null) {
\r
1813 dset = new DataSet ();
\r
1814 dset.Tables.Add (this);
\r
1815 isPartOfDataSet = false;
\r
1818 XmlSchemaWriter.WriteXmlSchema (writer, new DataTable [] { this },
\r
1819 null, TableName, dset.DataSetName, LocaleSpecified ? Locale : dset.LocaleSpecified ? dset.Locale : null);
\r
1820 dset.WriteIndividualTableContent (writer, this, XmlWriteMode.DiffGram);
\r
1823 if (!isPartOfDataSet)
\r
1824 dataSet.Tables.Remove(this);
\r
1828 protected virtual XmlSchema GetSchema ()
\r
1830 throw new NotImplementedException ();
\r
1833 public static XmlSchemaComplexType GetDataTableSchema (XmlSchemaSet schemaSet)
\r
1835 return new XmlSchemaComplexType ();
\r
1838 public XmlReadMode ReadXml (Stream stream)
\r
1840 return ReadXml (new XmlTextReader(stream, null));
\r
1843 public XmlReadMode ReadXml (string fileName)
\r
1845 XmlReader reader = new XmlTextReader (fileName);
\r
1847 return ReadXml (reader);
\r
1853 public XmlReadMode ReadXml (TextReader reader)
\r
1855 return ReadXml (new XmlTextReader (reader));
\r
1858 public XmlReadMode ReadXml (XmlReader reader)
\r
1860 return ReadXml_internal (reader, false);
\r
1863 public XmlReadMode ReadXml_internal (XmlReader reader, bool serializable)
\r
1865 // The documentation from MS for this method is rather
\r
1866 // poor. The following cases have been observed
\r
1867 // during testing:
\r
1869 // Reading a table from XML may create a DataSet to
\r
1870 // store child tables.
\r
1872 // If the table has at least one column present,
\r
1873 // we do not require the schema to be present in
\r
1874 // the xml. If the table has no columns, neither
\r
1875 // regular data nor diffgrams will be read, but
\r
1876 // will throw an error indicating that schema
\r
1877 // will not be inferred.
\r
1879 // We will likely need to take advantage of the
\r
1880 // msdata:MainDataTable attribute added to the
\r
1881 // schema info to load into the appropriate
\r
1883 bool isPartOfDataSet = true;
\r
1884 bool isTableNameBlank = false;
\r
1885 XmlReadMode mode = XmlReadMode.ReadSchema;
\r
1886 DataSet dataSet = null;
\r
1887 DataSet ds = new DataSet ();
\r
1889 reader.MoveToContent ();
\r
1890 if (Columns.Count > 0 && reader.LocalName != "diffgram" || serializable)
\r
1891 mode = ds.ReadXml (reader);
\r
1892 else if (Columns.Count > 0 && reader.LocalName == "diffgram") {
\r
1894 if (TableName == String.Empty)
\r
1895 isTableNameBlank = true;
\r
1896 if (DataSet == null) {
\r
1897 isPartOfDataSet = false;
\r
1898 ds.Tables.Add (this);
\r
1899 mode = ds.ReadXml (reader);
\r
1901 mode = DataSet.ReadXml (reader);
\r
1902 } catch (DataException) {
\r
1903 mode = XmlReadMode.DiffGram;
\r
1904 if (isTableNameBlank)
\r
1905 TableName = String.Empty;
\r
1907 if (!isPartOfDataSet)
\r
1908 ds.Tables.Remove (this);
\r
1912 mode = ds.ReadXml (reader, XmlReadMode.ReadSchema);
\r
1914 if (mode == XmlReadMode.InferSchema)
\r
1915 mode = XmlReadMode.IgnoreSchema;
\r
1916 if (DataSet == null) {
\r
1917 isPartOfDataSet = false;
\r
1918 dataSet = new DataSet ();
\r
1919 if (TableName == String.Empty)
\r
1920 isTableNameBlank = true;
\r
1921 dataSet.Tables.Add (this);
\r
1924 DenyXmlResolving (this, ds, mode, isTableNameBlank, isPartOfDataSet);
\r
1925 if (Columns.Count > 0 && TableName != ds.Tables [0].TableName) {
\r
1926 if (isPartOfDataSet == false)
\r
1927 dataSet.Tables.Remove (this);
\r
1929 if (isTableNameBlank && isPartOfDataSet == false)
\r
1930 TableName = String.Empty;
\r
1934 TableName = ds.Tables [0].TableName;
\r
1937 if (!isPartOfDataSet) {
\r
1938 if (Columns.Count > 0) {
\r
1939 dataSet.Merge (ds, true, MissingSchemaAction.Ignore);
\r
1941 dataSet.Merge (ds, true, MissingSchemaAction.AddWithKey);
\r
1943 if (ChildRelations.Count == 0) {
\r
1944 dataSet.Tables.Remove (this);
\r
1946 dataSet.DataSetName = ds.DataSetName;
\r
1949 if (Columns.Count > 0) {
\r
1950 DataSet.Merge (ds, true, MissingSchemaAction.Ignore);
\r
1952 DataSet.Merge (ds, true, MissingSchemaAction.AddWithKey);
\r
1958 private void DenyXmlResolving (DataTable table, DataSet ds, XmlReadMode mode, bool isTableNameBlank, bool isPartOfDataSet)
\r
1960 if (ds.Tables.Count == 0 && table.Columns.Count == 0)
\r
1961 throw new InvalidOperationException ("DataTable does not support schema inference from XML");
\r
1963 if (table.Columns.Count == 0 && ds.Tables [0].TableName != table.TableName && isTableNameBlank == false)
\r
1964 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",
\r
1965 table.TableName));
\r
1967 if (table.Columns.Count > 0 && ds.Tables [0].TableName != table.TableName &&
\r
1968 isTableNameBlank == false && mode == XmlReadMode.ReadSchema &&
\r
1969 isPartOfDataSet == false)
\r
1970 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",
\r
1971 table.TableName));
\r
1973 if (isPartOfDataSet == true && table.Columns.Count > 0 &&
\r
1974 mode == XmlReadMode.ReadSchema && table.TableName != ds.Tables [0].TableName)
\r
1975 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source",
\r
1976 table.TableName));
\r
1979 public void ReadXmlSchema (Stream stream)
\r
1981 ReadXmlSchema (new XmlTextReader (stream));
\r
1984 public void ReadXmlSchema (TextReader reader)
\r
1986 ReadXmlSchema (new XmlTextReader (reader));
\r
1989 public void ReadXmlSchema (string fileName)
\r
1991 XmlTextReader reader = null;
\r
1993 reader = new XmlTextReader (fileName);
\r
1994 ReadXmlSchema (reader);
\r
1996 if (reader != null)
\r
2001 private bool ReadSchemaElement (XmlReader reader)
\r
2003 var insideElement = false;
\r
2004 reader.MoveToElement ();
\r
2005 while (reader.Read ())
\r
2007 if (reader.NodeType == XmlNodeType.Element || reader.NodeType == XmlNodeType.EndElement)
\r
2009 if (reader.NamespaceURI != XmlSchema.Namespace)
\r
2011 if (reader.LocalName == "schema" || insideElement)
\r
2012 throw new ArgumentException ("The schema namespace is invalid. Please use this one instead: " + XmlSchema.Namespace);
\r
2014 insideElement = true;
\r
2015 reader.MoveToElement ();
\r
2026 public void ReadXmlSchema (XmlReader reader)
\r
2028 if (this.Columns.Count > 0)
\r
2031 DataSet ds = new DataSet ();
\r
2033 if (reader.ReadState == ReadState.Initial && !ReadSchemaElement (reader))
\r
2036 new XmlSchemaDataImporter (ds, reader, false).Process ();
\r
2037 DataTable target = null;
\r
2038 if (TableName == String.Empty) {
\r
2039 if (ds.Tables.Count > 0)
\r
2040 target = ds.Tables [0];
\r
2043 target = ds.Tables [TableName];
\r
2044 if (target == null)
\r
2045 throw new ArgumentException (String.Format ("DataTable '{0}' does not match to any DataTable in source.", TableName));
\r
2047 if (target != null)
\r
2048 target.CopyProperties (this);
\r
2051 [MonoNotSupported ("")]
\r
2052 protected virtual void ReadXmlSerializable (XmlReader reader)
\r
2054 throw new NotImplementedException ();
\r
2057 private XmlWriterSettings GetWriterSettings ()
\r
2059 XmlWriterSettings s = new XmlWriterSettings ();
\r
2061 s.OmitXmlDeclaration = true;
\r
2065 public void WriteXml (Stream stream)
\r
2067 WriteXml (stream, XmlWriteMode.IgnoreSchema, false);
\r
2070 public void WriteXml (TextWriter writer)
\r
2072 WriteXml (writer, XmlWriteMode.IgnoreSchema, false);
\r
2075 public void WriteXml (XmlWriter writer)
\r
2077 WriteXml (writer, XmlWriteMode.IgnoreSchema, false);
\r
2080 public void WriteXml (string fileName)
\r
2082 WriteXml (fileName, XmlWriteMode.IgnoreSchema, false);
\r
2085 public void WriteXml (Stream stream, XmlWriteMode mode)
\r
2087 WriteXml (stream, mode, false);
\r
2090 public void WriteXml (TextWriter writer, XmlWriteMode mode)
\r
2092 WriteXml (writer, mode, false);
\r
2095 public void WriteXml (XmlWriter writer, XmlWriteMode mode)
\r
2097 WriteXml (writer, mode, false);
\r
2100 public void WriteXml (string fileName, XmlWriteMode mode)
\r
2102 WriteXml (fileName, mode, false);
\r
2105 public void WriteXml (Stream stream, bool writeHierarchy)
\r
2107 WriteXml (stream, XmlWriteMode.IgnoreSchema, writeHierarchy);
\r
2110 public void WriteXml (string fileName, bool writeHierarchy)
\r
2112 WriteXml (fileName, XmlWriteMode.IgnoreSchema, writeHierarchy);
\r
2115 public void WriteXml (TextWriter writer, bool writeHierarchy)
\r
2117 WriteXml (writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
\r
2120 public void WriteXml (XmlWriter writer, bool writeHierarchy)
\r
2122 WriteXml (writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
\r
2125 public void WriteXml (Stream stream, XmlWriteMode mode, bool writeHierarchy)
\r
2127 WriteXml (XmlWriter.Create (stream, GetWriterSettings ()), mode, writeHierarchy);
\r
2130 public void WriteXml (string fileName, XmlWriteMode mode, bool writeHierarchy)
\r
2132 XmlWriter xw = null;
\r
2134 xw = XmlWriter.Create (fileName, GetWriterSettings ());
\r
2135 WriteXml (xw, mode, writeHierarchy);
\r
2142 public void WriteXml (TextWriter writer, XmlWriteMode mode, bool writeHierarchy)
\r
2144 WriteXml (XmlWriter.Create (writer, GetWriterSettings ()), mode, writeHierarchy);
\r
2147 public void WriteXml (XmlWriter writer, XmlWriteMode mode, bool writeHierarchy)
\r
2149 // If we're in mode XmlWriteMode.WriteSchema, we need to output an extra
\r
2150 // msdata:MainDataTable attribute that wouldn't normally be part of the
\r
2151 // DataSet WriteXml output.
\r
2153 // For the writeHierarchy == true case, we write what would be output by
\r
2154 // a DataSet write, but we limit ourselves to our table and its descendants.
\r
2156 // For the writeHierarchy == false case, we write what would be output by
\r
2157 // a DataSet write, but we limit ourselves to this table.
\r
2159 // If the table is not in a DataSet, we follow the following behaviour:
\r
2160 // For WriteSchema cases, we do a write as if there is a wrapper
\r
2161 // dataset called NewDataSet.
\r
2162 // For IgnoreSchema or DiffGram cases, we do a write as if there
\r
2163 // is a wrapper dataset called DocumentElement.
\r
2165 // Generate a list of tables to write.
\r
2166 List <DataTable> tables = new List <DataTable> ();
\r
2167 if (writeHierarchy == false)
\r
2168 tables.Add (this);
\r
2170 FindAllChildren (tables, this);
\r
2172 // If we're in a DataSet, generate a list of relations to write.
\r
2173 List <DataRelation> relations = new List <DataRelation> ();
\r
2174 if (DataSet != null) {
\r
2175 foreach (DataRelation relation in DataSet.Relations) {
\r
2176 if (tables.Contains (relation.ParentTable) &&
\r
2177 tables.Contains (relation.ChildTable))
\r
2178 relations.Add (relation);
\r
2182 // Add the msdata:MainDataTable info if we're writing schema data.
\r
2183 string mainDataTable = null;
\r
2184 if (mode == XmlWriteMode.WriteSchema)
\r
2185 mainDataTable = this.TableName;
\r
2187 // Figure out the DataSet name.
\r
2188 string dataSetName = null;
\r
2189 if (DataSet != null)
\r
2190 dataSetName = DataSet.DataSetName;
\r
2191 else if (DataSet == null && mode == XmlWriteMode.WriteSchema)
\r
2192 dataSetName = "NewDataSet";
\r
2194 dataSetName = "DocumentElement";
\r
2196 XmlTableWriter.WriteTables(writer, mode, tables, relations, mainDataTable, dataSetName);
\r
2199 private void FindAllChildren(List<DataTable> list, DataTable root)
\r
2201 if (!list.Contains(root))
\r
2204 foreach (DataRelation relation in root.ChildRelations)
\r
2206 FindAllChildren(list, relation.ChildTable);
\r
2211 public void WriteXmlSchema (Stream stream)
\r
2213 if (TableName == "") {
\r
2214 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2216 XmlWriterSettings s = GetWriterSettings ();
\r
2217 s.OmitXmlDeclaration = false;
\r
2218 WriteXmlSchema (XmlWriter.Create (stream, s));
\r
2221 public void WriteXmlSchema (TextWriter writer)
\r
2223 if (TableName == "") {
\r
2224 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2226 XmlWriterSettings s = GetWriterSettings ();
\r
2227 s.OmitXmlDeclaration = false;
\r
2228 WriteXmlSchema (XmlWriter.Create (writer, s));
\r
2231 public void WriteXmlSchema (XmlWriter writer)
\r
2233 WriteXmlSchema (writer, false);
\r
2235 if (TableName == "") {
\r
2236 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2238 DataSet ds = DataSet;
\r
2239 DataSet tmp = null;
\r
2242 tmp = ds = new DataSet ();
\r
2243 ds.Tables.Add (this);
\r
2245 writer.WriteStartDocument ();
\r
2246 DataTableCollection col = new DataTableCollection (ds);
\r
2248 DataTable [] tables = new DataTable [col.Count];
\r
2249 for (int i = 0; i < col.Count; i++) tables[i] = col[i];
\r
2251 if (ds.Namespace == "")
\r
2252 tableName = TableName;
\r
2254 tableName = ds.Namespace + "_x003A_" + TableName;
\r
2255 XmlSchemaWriter.WriteXmlSchema (writer, tables, null, tableName, ds.DataSetName);
\r
2258 ds.Tables.Remove (this);
\r
2263 public void WriteXmlSchema (string fileName)
\r
2265 if (fileName == "") {
\r
2266 throw new ArgumentException ("Empty path name is not legal.");
\r
2268 if (TableName == "") {
\r
2269 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2271 XmlTextWriter writer = null;
\r
2273 XmlWriterSettings s = GetWriterSettings ();
\r
2274 s.OmitXmlDeclaration = false;
\r
2275 writer = new XmlTextWriter (fileName, null);
\r
2276 WriteXmlSchema (writer);
\r
2278 if (writer != null) {
\r
2284 public void WriteXmlSchema (Stream stream, bool writeHierarchy)
\r
2286 if (TableName == "") {
\r
2287 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2289 XmlWriterSettings s = GetWriterSettings ();
\r
2290 s.OmitXmlDeclaration = false;
\r
2291 WriteXmlSchema (XmlWriter.Create (stream, s), writeHierarchy);
\r
2294 public void WriteXmlSchema (TextWriter writer, bool writeHierarchy)
\r
2296 if (TableName == "") {
\r
2297 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2299 XmlWriterSettings s = GetWriterSettings ();
\r
2300 s.OmitXmlDeclaration = false;
\r
2301 WriteXmlSchema (XmlWriter.Create (writer, s), writeHierarchy);
\r
2304 public void WriteXmlSchema (XmlWriter writer, bool writeHierarchy)
\r
2306 if (TableName == "") {
\r
2307 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2309 // if (writeHierarchy == false) {
\r
2310 // WriteXmlSchema (writer);
\r
2313 DataSet ds = DataSet;
\r
2314 DataSet tmp = null;
\r
2317 tmp = ds = new DataSet ();
\r
2318 ds.Tables.Add (this);
\r
2320 writer.WriteStartDocument ();
\r
2321 //XmlSchemaWriter.WriteXmlSchema (ds, writer);
\r
2322 //DataTable [] tables = new DataTable [ds.Tables.Count];
\r
2323 DataTable [] tables = null;
\r
2324 //DataRelation [] relations = new DataRelation [ds.Relations.Count];
\r
2325 //for (int i = 0; i < ds.Relations.Count; i++) {
\r
2326 // relations[i] = ds.Relations[i];
\r
2328 DataRelation [] relations = null;
\r
2329 if (writeHierarchy && ChildRelations.Count > 0) {
\r
2330 relations = new DataRelation [ChildRelations.Count];
\r
2331 for (int i = 0; i < ChildRelations.Count; i++) {
\r
2332 relations [i] = ChildRelations [i];
\r
2334 tables = new DataTable [ds.Tables.Count];
\r
2335 for (int i = 0; i < ds.Tables.Count; i++) tables [i] = ds.Tables [i];
\r
2337 tables = new DataTable [1];
\r
2338 tables [0] = this;
\r
2342 if (ds.Namespace == "")
\r
2343 tableName = TableName;
\r
2345 tableName = ds.Namespace + "_x003A_" + TableName;
\r
2346 XmlSchemaWriter.WriteXmlSchema (writer, tables, relations, tableName, ds.DataSetName, LocaleSpecified ? Locale : ds.LocaleSpecified ? ds.Locale : null);
\r
2349 ds.Tables.Remove (this);
\r
2354 public void WriteXmlSchema (string fileName, bool writeHierarchy)
\r
2356 if (fileName == "") {
\r
2357 throw new ArgumentException ("Empty path name is not legal.");
\r
2359 if (TableName == "") {
\r
2360 throw new InvalidOperationException ("Cannot serialize the DataTable. DataTable name is not set.");
\r
2362 XmlTextWriter writer = null;
\r
2364 XmlWriterSettings s = GetWriterSettings ();
\r
2365 s.OmitXmlDeclaration = false;
\r
2366 writer = new XmlTextWriter (fileName, null);
\r
2367 WriteXmlSchema (writer, writeHierarchy);
\r
2369 if (writer != null) {
\r
2376 partial class DataTable : ISupportInitializeNotification {
\r
2377 private bool tableInitialized = true;
\r
2379 [Browsable (false)]
\r
2380 public bool IsInitialized {
\r
2381 get { return tableInitialized; }
\r
2384 public event EventHandler Initialized;
\r
2386 private void OnTableInitialized (EventArgs e)
\r
2388 if (null != Initialized)
\r
2389 Initialized (this, e);
\r
2392 partial void DataTableInitialized ()
\r
2394 tableInitialized = true;
\r
2395 OnTableInitialized (new EventArgs ());
\r
2399 partial class DataTable {
\r
2400 public DataTable (string tableName, string tableNamespace)
\r
2401 : this (tableName)
\r
2403 _nameSpace = tableNamespace;
\r
2406 SerializationFormat remotingFormat = SerializationFormat.Xml;
\r
2407 [DefaultValue (SerializationFormat.Xml)]
\r
2408 public SerializationFormat RemotingFormat {
\r
2410 if (dataSet != null)
\r
2411 remotingFormat = dataSet.RemotingFormat;
\r
2412 return remotingFormat;
\r
2415 if (dataSet != null)
\r
2416 throw new ArgumentException ("Cannot have different remoting format property value for DataSet and DataTable");
\r
2417 remotingFormat = value;
\r
2421 internal void DeserializeConstraints (ArrayList arrayList)
\r
2423 bool fKeyNavigate = false;
\r
2424 for (int i = 0; i < arrayList.Count; i++) {
\r
2425 ArrayList tmpArrayList = arrayList [i] as ArrayList;
\r
2426 if (tmpArrayList == null)
\r
2428 if ((string) tmpArrayList [0] == "F") {
\r
2429 int [] constraintsArray = tmpArrayList [2] as int [];
\r
2431 if (constraintsArray == null)
\r
2433 ArrayList parentColumns = new ArrayList ();
\r
2434 DataTable dt = dataSet.Tables [constraintsArray [0]];
\r
2435 for (int j = 0; j < constraintsArray.Length - 1; j++) {
\r
2436 parentColumns.Add (dt.Columns [constraintsArray [j + 1]]);
\r
2439 constraintsArray = tmpArrayList [3] as int [];
\r
2441 if (constraintsArray == null)
\r
2443 ArrayList childColumns = new ArrayList ();
\r
2444 dt = dataSet.Tables [constraintsArray [0]];
\r
2445 for (int j = 0; j < constraintsArray.Length - 1; j++) {
\r
2446 childColumns.Add (dt.Columns [constraintsArray [j + 1]]);
\r
2449 ForeignKeyConstraint fKeyConstraint = new
\r
2450 ForeignKeyConstraint ((string) tmpArrayList [1],
\r
2451 (DataColumn []) parentColumns.ToArray (typeof (DataColumn)),
\r
2452 (DataColumn []) childColumns.ToArray (typeof (DataColumn)));
\r
2453 Array ruleArray = (Array) tmpArrayList [4];
\r
2454 fKeyConstraint.AcceptRejectRule = (AcceptRejectRule) ruleArray.GetValue (0);
\r
2455 fKeyConstraint.UpdateRule = (Rule) ruleArray.GetValue (1);
\r
2456 fKeyConstraint.DeleteRule = (Rule) ruleArray.GetValue (2);
\r
2457 // FIXME: refactor this deserialization code out to ForeighKeyConstraint
\r
2458 fKeyConstraint.SetExtendedProperties ((PropertyCollection) tmpArrayList [5]);
\r
2459 Constraints.Add (fKeyConstraint);
\r
2460 fKeyNavigate = true;
\r
2461 } else if (fKeyNavigate == false &&
\r
2462 (string) tmpArrayList [0] == "U") {
\r
2463 UniqueConstraint unique = null;
\r
2464 ArrayList uniqueDataColumns = new ArrayList ();
\r
2465 int [] constraintsArray = tmpArrayList [2] as int [];
\r
2467 if (constraintsArray == null)
\r
2470 for (int j = 0; j < constraintsArray.Length; j++) {
\r
2471 uniqueDataColumns.Add (Columns [constraintsArray [j]]);
\r
2473 unique = new UniqueConstraint ((string) tmpArrayList[1],
\r
2474 (DataColumn[]) uniqueDataColumns.
\r
2475 ToArray (typeof (DataColumn)),
\r
2476 (bool) tmpArrayList [3]);
\r
2478 If UniqueConstraint already exist, don't add them
\r
2480 if (Constraints.IndexOf (unique) != -1 ||
\r
2481 Constraints.IndexOf ((string) tmpArrayList[1]) != -1) {
\r
2484 // FIXME: refactor this deserialization code out to UniqueConstraint
\r
2485 unique.SetExtendedProperties ((PropertyCollection) tmpArrayList [4]);
\r
2486 Constraints.Add (unique);
\r
2488 fKeyNavigate = false;
\r
2493 DataRowState GetCurrentRowState (BitArray rowStateBitArray, int i)
\r
2495 DataRowState currentRowState;
\r
2496 if (rowStateBitArray[i] == false &&
\r
2497 rowStateBitArray[i+1] == false &&
\r
2498 rowStateBitArray[i+2] == true)
\r
2499 currentRowState = DataRowState.Detached;
\r
2500 else if (rowStateBitArray[i] == false &&
\r
2501 rowStateBitArray[i+1] == false &&
\r
2502 rowStateBitArray[i+2] == false)
\r
2503 currentRowState = DataRowState.Unchanged;
\r
2504 else if (rowStateBitArray[i] == false &&
\r
2505 rowStateBitArray[i+1] == true &&
\r
2506 rowStateBitArray[i+2] == false)
\r
2507 currentRowState = DataRowState.Added;
\r
2508 else if (rowStateBitArray[i] == true &&
\r
2509 rowStateBitArray[i+1] == true &&
\r
2510 rowStateBitArray[i+2] == false)
\r
2511 currentRowState = DataRowState.Deleted;
\r
2513 currentRowState = DataRowState.Modified;
\r
2514 return currentRowState;
\r
2517 internal void DeserializeRecords (ArrayList arrayList, ArrayList nullBits, BitArray rowStateBitArray)
\r
2519 BitArray nullBit = null;
\r
2520 if (arrayList == null || arrayList.Count < 1)
\r
2522 int len = ((Array) arrayList [0]).Length;
\r
2523 object [] tmpArray = new object [arrayList.Count];
\r
2525 DataRowState currentRowState;
\r
2526 for (int l = 0; l < len; l++) { // Columns
\r
2527 currentRowState = GetCurrentRowState (rowStateBitArray, k *3);
\r
2528 for (int j = 0; j < arrayList.Count; j++) { // Rows
\r
2529 Array array = (Array) arrayList [j];
\r
2530 nullBit = (BitArray) nullBits [j];
\r
2531 if (nullBit [l] == false) {
\r
2532 tmpArray [j] = array.GetValue (l);
\r
2534 tmpArray [j] = null;
\r
2537 LoadDataRow (tmpArray, false);
\r
2538 if (currentRowState == DataRowState.Modified) {
\r
2539 Rows[k].AcceptChanges ();
\r
2541 for (int j = 0; j < arrayList.Count; j++) {
\r
2542 Array array = (Array) arrayList [j];
\r
2543 nullBit = (BitArray) nullBits[j];
\r
2544 if (nullBit [l] == false) {
\r
2545 Rows [k][j] = array.GetValue (l);
\r
2547 Rows [k][j] = null;
\r
2550 } else if (currentRowState == DataRowState.Unchanged) {
\r
2551 Rows[k].AcceptChanges ();
\r
2552 } else if (currentRowState == DataRowState.Deleted) {
\r
2553 Rows[k].AcceptChanges ();
\r
2554 Rows[k].Delete ();
\r
2560 void BinaryDeserializeTable (SerializationInfo info)
\r
2562 ArrayList arrayList = null;
\r
2564 TableName = info.GetString ("DataTable.TableName");
\r
2565 Namespace = info.GetString ("DataTable.Namespace");
\r
2566 Prefix = info.GetString ("DataTable.Prefix");
\r
2567 CaseSensitive = info.GetBoolean ("DataTable.CaseSensitive");
\r
2569 FIXME: Private variable available in SerializationInfo
\r
2570 this.caseSensitiveAmbientCaseSensitive = info.GetBoolean("DataTable.caseSensitiveAmbientCaseSensitive");
\r
2571 this.NestedInDataSet = info.GetBoolean("DataTable.NestedInDataSet");
\r
2572 this.RepeatableElement = info.GetBoolean("DataTable.RepeatableElement");
\r
2573 this.RemotingVersion = (System.Version) info.GetValue("DataTable.RemotingVersion",
\r
2574 typeof(System.Version));
\r
2576 Locale = new CultureInfo (info.GetInt32 ("DataTable.LocaleLCID"));
\r
2577 _extendedProperties = (PropertyCollection) info.GetValue ("DataTable.ExtendedProperties",
\r
2578 typeof (PropertyCollection));
\r
2579 MinimumCapacity = info.GetInt32 ("DataTable.MinimumCapacity");
\r
2580 int columnsCount = info.GetInt32 ("DataTable.Columns.Count");
\r
2582 for (int i = 0; i < columnsCount; i++) {
\r
2584 string prefix = "DataTable.DataColumn_" + i + ".";
\r
2585 Columns[i].ColumnName = info.GetString (prefix + "ColumnName");
\r
2586 Columns[i].Namespace = info.GetString (prefix + "Namespace");
\r
2587 Columns[i].Caption = info.GetString (prefix + "Caption");
\r
2588 Columns[i].Prefix = info.GetString (prefix + "Prefix");
\r
2589 Columns[i].DataType = (Type) info.GetValue (prefix + "DataType",
\r
2591 Columns[i].DefaultValue = info.GetValue (prefix + "DefaultValue",
\r
2593 Columns[i].AllowDBNull = info.GetBoolean (prefix + "AllowDBNull");
\r
2594 Columns[i].AutoIncrement = info.GetBoolean (prefix + "AutoIncrement");
\r
2595 Columns[i].AutoIncrementStep = info.GetInt64 (prefix + "AutoIncrementStep");
\r
2596 Columns[i].AutoIncrementSeed = info.GetInt64(prefix + "AutoIncrementSeed");
\r
2597 Columns[i].ReadOnly = info.GetBoolean (prefix + "ReadOnly");
\r
2598 Columns[i].MaxLength = info.GetInt32 (prefix + "MaxLength");
\r
2600 FIXME: Private variable available in SerializationInfo
\r
2601 this.Columns[i].SimpleType = info.GetString("DataTable.DataColumn_" +
\r
2602 i + ".SimpleType");
\r
2603 this.Columns[i].AutoIncrementCurrent = info.GetInt64("DataTable.DataColumn_" +
\r
2604 i + ".AutoIncrementCurrent");
\r
2605 this.Columns[i].XmlDataType = info.GetString("DataTable.DataColumn_" +
\r
2606 i + ".XmlDataType");
\r
2608 Columns[i].ExtendedProperties = (PropertyCollection) info.GetValue (prefix + "ExtendedProperties",
\r
2609 typeof (PropertyCollection));
\r
2610 if (Columns[i].DataType == typeof (DataSetDateTime)) {
\r
2611 Columns[i].DateTimeMode = (DataSetDateTime) info.GetValue (prefix + "DateTimeMode",
\r
2612 typeof (DataSetDateTime));
\r
2614 Columns[i].ColumnMapping = (MappingType) info.GetValue (prefix + "ColumnMapping",
\r
2615 typeof (MappingType));
\r
2617 Columns[i].Expression = info.GetString (prefix + "Expression");
\r
2619 prefix = "DataTable_0.";
\r
2621 arrayList = (ArrayList) info.GetValue (prefix + "Constraints",
\r
2622 typeof (ArrayList));
\r
2623 if (Constraints == null)
\r
2624 Constraints = new ConstraintCollection (this);
\r
2625 DeserializeConstraints (arrayList);
\r
2626 } catch (SerializationException) {
\r
2630 String prefix = "DataTable_0.";
\r
2631 ArrayList nullBits = (ArrayList) info.GetValue (prefix + "NullBits",
\r
2632 typeof (ArrayList));
\r
2633 arrayList = (ArrayList) info.GetValue (prefix + "Records",
\r
2634 typeof (ArrayList));
\r
2635 BitArray rowStateBitArray = (BitArray) info.GetValue (prefix + "RowStates",
\r
2636 typeof (BitArray));
\r
2637 Hashtable htRowErrors = (Hashtable) info.GetValue (prefix + "RowErrors",
\r
2638 typeof (Hashtable));
\r
2639 DeserializeRecords (arrayList, nullBits, rowStateBitArray);
\r
2640 } catch (SerializationException) {
\r
2644 internal void BinarySerializeProperty (SerializationInfo info)
\r
2646 Version vr = new Version (2, 0);
\r
2647 info.AddValue ("DataTable.RemotingVersion", vr);
\r
2648 info.AddValue ("DataTable.RemotingFormat", RemotingFormat);
\r
2649 info.AddValue ("DataTable.TableName", TableName);
\r
2650 info.AddValue ("DataTable.Namespace", Namespace);
\r
2651 info.AddValue ("DataTable.Prefix", Prefix);
\r
2652 info.AddValue ("DataTable.CaseSensitive", CaseSensitive);
\r
2654 FIXME: Required by MS.NET
\r
2655 caseSensitiveAmbient, NestedInDataSet, RepeatableElement
\r
2657 info.AddValue ("DataTable.caseSensitiveAmbient", true);
\r
2658 info.AddValue ("DataTable.NestedInDataSet", true);
\r
2659 info.AddValue ("DataTable.RepeatableElement", false);
\r
2660 info.AddValue ("DataTable.LocaleLCID", Locale.LCID);
\r
2661 info.AddValue ("DataTable.MinimumCapacity", MinimumCapacity);
\r
2662 info.AddValue ("DataTable.Columns.Count", Columns.Count);
\r
2663 info.AddValue ("DataTable.ExtendedProperties", _extendedProperties);
\r
2664 for (int i = 0; i < Columns.Count; i++) {
\r
2665 info.AddValue ("DataTable.DataColumn_" + i + ".ColumnName",
\r
2666 Columns[i].ColumnName);
\r
2667 info.AddValue ("DataTable.DataColumn_" + i + ".Namespace",
\r
2668 Columns[i].Namespace);
\r
2669 info.AddValue ("DataTable.DataColumn_" + i + ".Caption",
\r
2670 Columns[i].Caption);
\r
2671 info.AddValue ("DataTable.DataColumn_" + i + ".Prefix",
\r
2672 Columns[i].Prefix);
\r
2673 info.AddValue ("DataTable.DataColumn_" + i + ".DataType",
\r
2674 Columns[i].DataType, typeof (Type));
\r
2675 info.AddValue ("DataTable.DataColumn_" + i + ".DefaultValue",
\r
2676 Columns[i].DefaultValue, typeof (DBNull));
\r
2677 info.AddValue ("DataTable.DataColumn_" + i + ".AllowDBNull",
\r
2678 Columns[i].AllowDBNull);
\r
2679 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrement",
\r
2680 Columns[i].AutoIncrement);
\r
2681 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementStep",
\r
2682 Columns[i].AutoIncrementStep);
\r
2683 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementSeed",
\r
2684 Columns[i].AutoIncrementSeed);
\r
2685 info.AddValue ("DataTable.DataColumn_" + i + ".ReadOnly",
\r
2686 Columns[i].ReadOnly);
\r
2687 info.AddValue ("DataTable.DataColumn_" + i + ".MaxLength",
\r
2688 Columns[i].MaxLength);
\r
2689 info.AddValue ("DataTable.DataColumn_" + i + ".ExtendedProperties",
\r
2690 Columns[i].ExtendedProperties);
\r
2691 info.AddValue ("DataTable.DataColumn_" + i + ".DateTimeMode",
\r
2692 Columns[i].DateTimeMode);
\r
2693 info.AddValue ("DataTable.DataColumn_" + i + ".ColumnMapping",
\r
2694 Columns[i].ColumnMapping, typeof (MappingType));
\r
2696 FIXME: Required by MS.NET
\r
2697 SimpleType, AutoIncrementCurrent, XmlDataType
\r
2699 info.AddValue ("DataTable.DataColumn_" + i + ".SimpleType",
\r
2700 null, typeof (string));
\r
2701 info.AddValue ("DataTable.DataColumn_" + i + ".AutoIncrementCurrent",
\r
2702 Columns[i].AutoIncrementValue());
\r
2703 info.AddValue ("DataTable.DataColumn_" + i + ".XmlDataType",
\r
2704 null, typeof (string));
\r
2707 FIXME: Required by MS.NET
\r
2710 info.AddValue ("DataTable.TypeName", null, typeof (string));
\r
2713 internal void SerializeConstraints (SerializationInfo info, string prefix)
\r
2715 ArrayList constraintArrayList = new ArrayList ();
\r
2716 for (int j = 0; j < Constraints.Count; j++) {
\r
2717 ArrayList constraintList = new ArrayList ();
\r
2718 if (Constraints[j] is UniqueConstraint) {
\r
2719 constraintList.Add ("U");
\r
2720 UniqueConstraint unique = (UniqueConstraint) Constraints [j];
\r
2721 constraintList.Add (unique.ConstraintName);
\r
2722 DataColumn [] columns = unique.Columns;
\r
2723 int [] tmpArray = new int [columns.Length];
\r
2724 for (int k = 0; k < columns.Length; k++)
\r
2725 tmpArray [k] = unique.Table.Columns.IndexOf (unique.Columns [k]);
\r
2726 constraintList.Add (tmpArray);
\r
2727 constraintList.Add (unique.IsPrimaryKey);
\r
2728 constraintList.Add (unique.ExtendedProperties);
\r
2729 } else if (Constraints[j] is ForeignKeyConstraint) {
\r
2730 constraintList.Add ("F");
\r
2731 ForeignKeyConstraint fKey = (ForeignKeyConstraint) Constraints [j];
\r
2732 constraintList.Add (fKey.ConstraintName);
\r
2734 int [] tmpArray = new int [fKey.RelatedColumns.Length + 1];
\r
2735 tmpArray [0] = DataSet.Tables.IndexOf (fKey.RelatedTable);
\r
2736 for (int i = 0; i < fKey.Columns.Length; i++) {
\r
2737 tmpArray [i + 1] = fKey.RelatedColumns [i].Ordinal;
\r
2739 constraintList.Add (tmpArray);
\r
2741 tmpArray = new int [fKey.Columns.Length + 1];
\r
2742 tmpArray [0] = DataSet.Tables.IndexOf (fKey.Table);
\r
2743 for (int i = 0; i < fKey.Columns.Length; i++) {
\r
2744 tmpArray [i + 1] = fKey.Columns [i].Ordinal;
\r
2746 constraintList.Add (tmpArray);
\r
2748 tmpArray = new int [3];
\r
2749 tmpArray [0] = (int) fKey.AcceptRejectRule;
\r
2750 tmpArray [1] = (int) fKey.UpdateRule;
\r
2751 tmpArray [2] = (int) fKey.DeleteRule;
\r
2752 constraintList.Add (tmpArray);
\r
2754 constraintList.Add (fKey.ExtendedProperties);
\r
2758 constraintArrayList.Add (constraintList);
\r
2760 info.AddValue (prefix, constraintArrayList, typeof (ArrayList));
\r
2763 internal void BinarySerialize (SerializationInfo info, string prefix)
\r
2765 int columnsCount = Columns.Count;
\r
2766 int rowsCount = Rows.Count;
\r
2767 int recordsCount = Rows.Count;
\r
2768 ArrayList recordList = new ArrayList ();
\r
2769 ArrayList bitArrayList = new ArrayList ();
\r
2770 BitArray rowStateBitArray = new BitArray (rowsCount * 3);
\r
2771 for (int k = 0; k < Rows.Count; k++) {
\r
2772 if (Rows[k].RowState == DataRowState.Modified)
\r
2775 SerializeConstraints (info, prefix + "Constraints");
\r
2776 for (int j = 0; j < columnsCount; j++) {
\r
2777 if (rowsCount == 0)
\r
2779 BitArray nullBits = new BitArray (rowsCount);
\r
2780 DataColumn column = Columns [j];
\r
2781 Array recordArray = Array.CreateInstance (column.DataType, recordsCount);
\r
2782 for (int k = 0, l = 0; k < Rows.Count; k++, l++) {
\r
2783 DataRowVersion version;
\r
2784 DataRow dr = Rows[k];
\r
2785 if (dr.RowState == DataRowState.Modified) {
\r
2786 version = DataRowVersion.Default;
\r
2787 nullBits.Length = nullBits.Length + 1;
\r
2788 if (dr.IsNull (column, version) == false) {
\r
2789 nullBits [l] = false;
\r
2790 recordArray.SetValue (dr [j, version], l);
\r
2792 nullBits [l] = true;
\r
2795 version = DataRowVersion.Current;
\r
2796 } else if (dr.RowState == DataRowState.Deleted) {
\r
2797 version = DataRowVersion.Original;
\r
2799 version = DataRowVersion.Default;
\r
2801 if (dr.IsNull (column, version) == false) {
\r
2802 nullBits [l] = false;
\r
2803 recordArray.SetValue (dr [j, version], l);
\r
2805 nullBits [l] = true;
\r
2808 recordList.Add (recordArray);
\r
2809 bitArrayList.Add (nullBits);
\r
2811 for (int j = 0; j < Rows.Count; j++) {
\r
2813 DataRowState rowState = Rows [j].RowState;
\r
2814 if (rowState == DataRowState.Detached) {
\r
2815 rowStateBitArray [l] = false;
\r
2816 rowStateBitArray [l + 1] = false;
\r
2817 rowStateBitArray [l + 2] = true;
\r
2818 } else if (rowState == DataRowState.Unchanged) {
\r
2819 rowStateBitArray [l] = false;
\r
2820 rowStateBitArray [l + 1] = false;
\r
2821 rowStateBitArray [l + 2] = false;
\r
2822 } else if (rowState == DataRowState.Added) {
\r
2823 rowStateBitArray [l] = false;
\r
2824 rowStateBitArray [l + 1] = true;
\r
2825 rowStateBitArray [l + 2] = false;
\r
2826 } else if (rowState == DataRowState.Deleted) {
\r
2827 rowStateBitArray [l] = true;
\r
2828 rowStateBitArray [l + 1] = true;
\r
2829 rowStateBitArray [l + 2] = false;
\r
2831 rowStateBitArray [l] = true;
\r
2832 rowStateBitArray [l + 1] = false;
\r
2833 rowStateBitArray [l + 2] = false;
\r
2836 info.AddValue (prefix + "Rows.Count", Rows.Count);
\r
2837 info.AddValue (prefix + "Records.Count", recordsCount);
\r
2838 info.AddValue (prefix + "Records", recordList, typeof (ArrayList));
\r
2839 info.AddValue (prefix + "NullBits", bitArrayList, typeof (ArrayList));
\r
2840 info.AddValue (prefix + "RowStates",
\r
2841 rowStateBitArray, typeof (BitArray));
\r
2842 // FIXME: To get row errors
\r
2843 Hashtable htRowErrors = new Hashtable ();
\r
2844 info.AddValue (prefix + "RowErrors",
\r
2845 htRowErrors, typeof (Hashtable));
\r
2846 // FIXME: To get column errors
\r
2847 Hashtable htColumnErrors = new Hashtable ();
\r
2848 info.AddValue (prefix + "ColumnErrors",
\r
2849 htColumnErrors, typeof (Hashtable));
\r
2852 public DataTableReader CreateDataReader ()
\r
2854 return new DataTableReader (this);
\r
2858 /// Loads the table with the values from the reader
\r
2860 public void Load (IDataReader reader)
\r
2862 if (reader == null)
\r
2863 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");
\r
2864 Load (reader, LoadOption.PreserveChanges);
\r
2868 /// Loads the table with the values from the reader and the pattern
\r
2869 /// of the changes to the existing rows or the new rows are based on
\r
2870 /// the LoadOption passed.
\r
2872 public void Load (IDataReader reader, LoadOption loadOption)
\r
2874 if (reader == null)
\r
2875 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");
\r
2876 bool prevEnforceConstr = this.EnforceConstraints;
\r
2878 this.EnforceConstraints = false;
\r
2879 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,
\r
2880 MissingSchemaAction.AddWithKey,
\r
2881 MissingMappingAction.Passthrough,
\r
2882 new DataTableMappingCollection ());
\r
2883 DbDataAdapter.FillFromReader (this,
\r
2890 this.EnforceConstraints = prevEnforceConstr;
\r
2894 public virtual void Load (IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler)
\r
2896 if (reader == null)
\r
2897 throw new ArgumentNullException ("Value cannot be null. Parameter name: reader");
\r
2899 bool prevEnforceConstr = this.EnforceConstraints;
\r
2901 this.EnforceConstraints = false;
\r
2903 int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,
\r
2904 MissingSchemaAction.AddWithKey,
\r
2905 MissingMappingAction.Passthrough,
\r
2906 new DataTableMappingCollection ());
\r
2907 DbDataAdapter.FillFromReader (this,
\r
2915 this.EnforceConstraints = prevEnforceConstr;
\r
2920 /// Loads the given values into an existing row if matches or creates
\r
2921 /// the new row popluated with the values.
\r
2924 /// This method searches for the values using primary keys and it first
\r
2925 /// searches using the original values of the rows and if not found, it
\r
2926 /// searches using the current version of the row.
\r
2928 public DataRow LoadDataRow (object [] values, LoadOption loadOption)
\r
2930 DataRow row = null;
\r
2932 // Find Data DataRow
\r
2933 if (this.PrimaryKey.Length > 0) {
\r
2934 object [] keys = new object [PrimaryKey.Length];
\r
2935 for (int i = 0; i < PrimaryKey.Length; i++)
\r
2936 keys [i] = values [PrimaryKey [i].Ordinal];
\r
2938 row = Rows.Find (keys, DataViewRowState.OriginalRows);
\r
2940 row = Rows.Find (keys);
\r
2943 // If not found, add new row
\r
2944 if (row == null ||
\r
2945 (row.RowState == DataRowState.Deleted && loadOption == LoadOption.Upsert)) {
\r
2946 row = NewNotInitializedRow ();
\r
2947 row.ImportRecord (CreateRecord (values));
\r
2949 row.Validate (); // this adds to index ;-)
\r
2951 if (loadOption == LoadOption.OverwriteChanges ||
\r
2952 loadOption == LoadOption.PreserveChanges)
\r
2953 Rows.AddInternal (row, DataRowAction.ChangeCurrentAndOriginal);
\r
2955 Rows.AddInternal(row);
\r
2959 row.Load (values, loadOption);
\r
2964 public void Merge (DataTable table)
\r
2966 Merge (table, false, MissingSchemaAction.Add);
\r
2969 public void Merge (DataTable table, bool preserveChanges)
\r
2971 Merge (table, preserveChanges, MissingSchemaAction.Add);
\r
2974 public void Merge (DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
\r
2976 MergeManager.Merge (this, table, preserveChanges, missingSchemaAction);
\r
2979 internal int CompareRecords (int x, int y)
\r
2981 for (int col = 0; col < Columns.Count; col++) {
\r
2982 int res = Columns[col].DataContainer.CompareValues (x, y);
\r
2991 /// Occurs after the Clear method is called on the datatable.
\r
2993 [DataCategory ("Data")]
\r
2994 public event DataTableClearEventHandler TableCleared;
\r
2996 [DataCategory ("Data")]
\r
2997 public event DataTableClearEventHandler TableClearing;
\r
2999 public event DataTableNewRowEventHandler TableNewRow;
\r
3002 /// Raises TableCleared Event and delegates to subscribers
\r
3004 protected virtual void OnTableCleared (DataTableClearEventArgs e)
\r
3006 if (TableCleared != null)
\r
3007 TableCleared (this, e);
\r
3010 internal void DataTableCleared ()
\r
3012 OnTableCleared (new DataTableClearEventArgs (this));
\r
3015 protected virtual void OnTableClearing (DataTableClearEventArgs e)
\r
3017 if (TableClearing != null)
\r
3018 TableClearing (this, e);
\r
3021 internal void DataTableClearing ()
\r
3023 OnTableClearing (new DataTableClearEventArgs (this));
\r
3026 protected virtual void OnTableNewRow (DataTableNewRowEventArgs e)
\r
3028 if (null != TableNewRow)
\r
3029 TableNewRow (this, e);
\r
3032 partial void NewRowAdded (DataRow dr)
\r
3034 OnTableNewRow (new DataTableNewRowEventArgs (dr));
\r