2 // System.Data.DataTable.cs
5 // Franklin Wise <gracenote@earthlink.net>
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Daniel Morgan <danmorg@sc.rr.com>
8 // Rodrigo Moya <rodrigo@ximian.com>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002-2003
15 // Copyright (C) Daniel Morgan, 2002-2003
19 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 using System
.Data
.Common
;
43 using System
.Collections
;
44 using System
.ComponentModel
;
45 using System
.Globalization
;
47 using System
.Runtime
.Serialization
;
49 using System
.Xml
.Schema
;
50 using Mono
.Data
.SqlExpressions
;
52 namespace System
.Data
{
55 [DefaultEvent ("RowChanging")]
56 [DefaultProperty ("TableName")]
57 [DesignTimeVisible (false)]
58 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
60 public class DataTable
: MarshalByValueComponent
, IListSource
, ISupportInitialize
, ISerializable
64 internal DataSet dataSet
;
66 private bool _caseSensitive
;
67 private DataColumnCollection _columnCollection
;
68 private ConstraintCollection _constraintCollection
;
69 private DataView _defaultView
;
71 private string _displayExpression
;
72 private PropertyCollection _extendedProperties
;
73 private bool _hasErrors
;
74 private CultureInfo _locale
;
75 private int _minimumCapacity
;
76 private string _nameSpace
;
77 private DataRelationCollection _childRelations
;
78 private DataRelationCollection _parentRelations
;
79 private string _prefix
;
80 private DataColumn
[] _primaryKey
;
81 private DataRowCollection _rows
;
83 private string _tableName
;
84 private bool _containsListCollection
;
85 private string _encodedTableName
;
86 internal bool _duringDataLoad
;
87 internal bool _nullConstraintViolationDuringDataLoad
;
88 private bool dataSetPrevEnforceConstraints
;
89 private bool dataTablePrevEnforceConstraints
;
90 private bool enforceConstraints
= true;
91 private DataRowBuilder _rowBuilder
;
92 private ArrayList _indexes
;
93 private RecordCache _recordCache
;
94 private int _defaultValuesRowIndex
= -1;
95 protected internal bool fInitInProgress
;
97 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
98 // CaseSensitive property. So when you lost you virginity it's gone for ever
99 private bool _virginCaseSensitive
= true;
103 private delegate void PostEndInit();
106 /// Initializes a new instance of the DataTable class with no arguments.
111 _columnCollection
= new DataColumnCollection(this);
112 _constraintCollection
= new ConstraintCollection(this);
113 _extendedProperties
= new PropertyCollection();
116 _caseSensitive
= false; //default value
117 _displayExpression
= null;
120 _rows
= new DataRowCollection (this);
121 _indexes
= new ArrayList();
122 _recordCache
= new RecordCache(this);
124 //LAMESPEC: spec says 25 impl does 50
125 _minimumCapacity
= 50;
127 _childRelations
= new DataRelationCollection
.DataTableRelationCollection (this);
128 _parentRelations
= new DataRelationCollection
.DataTableRelationCollection (this);
130 _defaultView
= new DataView(this);
134 /// Intitalizes a new instance of the DataTable class with the specified table name.
136 public DataTable (string tableName
) : this ()
138 _tableName
= tableName
;
142 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
145 protected DataTable (SerializationInfo info
, StreamingContext context
)
148 string schema
= info
.GetString ("XmlSchema");
149 string data
= info
.GetString ("XmlDiffGram");
151 DataSet ds
= new DataSet ();
152 ds
.ReadXmlSchema (new StringReader (schema
));
153 ds
.Tables
[0].CopyProperties (this);
155 ds
.Tables
.Add (this);
156 ds
.ReadXml (new StringReader (data
), XmlReadMode
.DiffGram
);
157 ds
.Tables
.Remove (this);
158 /* keeping for a while. With the change above, we shouldn't have to consider
159 * DataTable mode in schema inference/read.
160 XmlSchemaMapper mapper = new XmlSchemaMapper (this);
161 XmlTextReader xtr = new XmlTextReader(new StringReader (schema));
164 XmlDiffLoader loader = new XmlDiffLoader (this);
165 xtr = new XmlTextReader(new StringReader (data));
171 public DataTable (string tableName
, string tbNamespace
)
174 _nameSpace
= tbNamespace
;
179 /// Indicates whether string comparisons within the table are case-sensitive.
181 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
182 public bool CaseSensitive
{
184 if (_virginCaseSensitive
&& dataSet
!= null)
185 return dataSet
.CaseSensitive
;
187 return _caseSensitive
;
190 if (_childRelations
.Count
> 0 || _parentRelations
.Count
> 0) {
191 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.");
193 _virginCaseSensitive
= false;
194 _caseSensitive
= value;
198 internal bool VirginCaseSensitive
{
199 get { return _virginCaseSensitive; }
200 set { _virginCaseSensitive = value; }
203 internal ArrayList Indexes
{
204 get { return _indexes; }
207 internal void ChangedDataColumn (DataRow dr
, DataColumn dc
, object pv
)
209 DataColumnChangeEventArgs e
= new DataColumnChangeEventArgs (dr
, dc
, pv
);
213 internal void ChangingDataColumn (DataRow dr
, DataColumn dc
, object pv
)
215 DataColumnChangeEventArgs e
= new DataColumnChangeEventArgs (dr
, dc
, pv
);
216 OnColumnChanging (e
);
219 internal void DeletedDataRow (DataRow dr
, DataRowAction action
)
221 DataRowChangeEventArgs e
= new DataRowChangeEventArgs (dr
, action
);
225 internal void DeletingDataRow (DataRow dr
, DataRowAction action
)
227 DataRowChangeEventArgs e
= new DataRowChangeEventArgs (dr
, action
);
231 internal void ChangedDataRow (DataRow dr
, DataRowAction action
)
233 DataRowChangeEventArgs e
= new DataRowChangeEventArgs (dr
, action
);
237 internal void ChangingDataRow (DataRow dr
, DataRowAction action
)
239 DataRowChangeEventArgs e
= new DataRowChangeEventArgs (dr
, action
);
244 /// Gets the collection of child relations for this DataTable.
247 [DataSysDescription ("Returns the child relations for this table.")]
248 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
249 public DataRelationCollection ChildRelations
{
251 return _childRelations
;
256 /// Gets the collection of columns that belong to this table.
258 [DataCategory ("Data")]
259 [DataSysDescription ("The collection that holds the columns for this table.")]
260 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Content
)]
261 public DataColumnCollection Columns
{
262 get { return _columnCollection; }
266 /// Gets the collection of constraints maintained by this table.
268 [DataCategory ("Data")]
269 [DataSysDescription ("The collection that holds the constraints for this table.")]
270 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Content
)]
271 public ConstraintCollection Constraints
{
272 get { return _constraintCollection; }
276 /// Gets the DataSet that this table belongs to.
279 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
280 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
281 public DataSet DataSet
{
282 get { return dataSet; }
288 /// Gets a customized view of the table which may
289 /// include a filtered view, or a cursor position.
293 [DataSysDescription ("This is the default DataView for the table.")]
294 public DataView DefaultView
{
295 get { return _defaultView; }
300 /// Gets or sets the expression that will return
301 /// a value used to represent this table in the user interface.
303 [DataCategory ("Data")]
304 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
306 public string DisplayExpression
{
307 get { return _displayExpression == null ? "" : _displayExpression; }
308 set { _displayExpression = value; }
312 /// Gets the collection of customized user information.
315 [DataCategory ("Data")]
316 [DataSysDescription ("The collection that holds custom user information.")]
317 public PropertyCollection ExtendedProperties
{
318 get { return _extendedProperties; }
322 /// Gets a value indicating whether there are errors in
323 /// any of the_rows in any of the tables of the DataSet to
324 /// which the table belongs.
327 [DataSysDescription ("Returns whether the table has errors.")]
328 public bool HasErrors
{
330 // we can not use the _hasError flag because we do not know when to turn it off!
331 for (int i
= 0; i
< _rows
.Count
; i
++)
333 if (_rows
[i
].HasErrors
)
341 /// Gets or sets the locale information used to
342 /// compare strings within the table.
344 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
345 public CultureInfo Locale
{
347 // if the locale is null, we check for the DataSet locale
348 // and if the DataSet is null we return the current culture.
349 // this way if DataSet locale is changed, only if there is no locale for
350 // the DataTable it influece the Locale get;
354 return DataSet
.Locale
;
355 return CultureInfo
.CurrentCulture
;
358 if (_childRelations
.Count
> 0 || _parentRelations
.Count
> 0) {
359 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.");
361 if (_locale
== null || !_locale
.Equals(value))
367 /// Gets or sets the initial starting size for this table.
369 [DataCategory ("Data")]
370 [DataSysDescription ("Indicates an initial starting size for this table.")]
372 public int MinimumCapacity
{
373 get { return _minimumCapacity; }
374 set { _minimumCapacity = value; }
378 /// Gets or sets the namespace for the XML represenation
379 /// of the data stored in the DataTable.
381 [DataCategory ("Data")]
382 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
383 public string Namespace
{
385 if (_nameSpace
!= null)
387 else if (dataSet
!= null)
388 return dataSet
.Namespace
;
391 set { _nameSpace = value; }
395 /// Gets the collection of parent relations for
399 [DataSysDescription ("Returns the parent relations for this table.")]
400 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
401 public DataRelationCollection ParentRelations
{
403 return _parentRelations
;
408 /// Gets or sets the namespace for the XML represenation
409 /// of the data stored in the DataTable.
411 [DataCategory ("Data")]
412 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
414 public string Prefix
{
415 get { return _prefix == null ? "" : _prefix; }
417 // Prefix cannot contain any special characters other than '_' and ':'
418 for (int i
= 0; i
< value.Length
; i
++) {
419 if (!(Char
.IsLetterOrDigit (value [i
])) && (value [i
] != '_') && (value [i
] != ':'))
420 throw new DataException ("Prefix '" + value + "' is not valid, because it contains special characters.");
427 /// Gets or sets an array of columns that function as
428 /// primary keys for the data table.
430 [DataCategory ("Data")]
431 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
432 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
433 [TypeConverterAttribute ("System.Data.PrimaryKeyTypeConverter, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
434 public DataColumn
[] PrimaryKey
{
436 UniqueConstraint uc
= UniqueConstraint
.GetPrimaryKeyConstraint( Constraints
);
437 if (null == uc
) return new DataColumn
[] {};
441 UniqueConstraint oldPKConstraint
= UniqueConstraint
.GetPrimaryKeyConstraint( Constraints
);
443 // first check if value is the same as current PK.
444 if (oldPKConstraint
!= null && DataColumn
.AreColumnSetsTheSame(value, oldPKConstraint
.Columns
))
447 // remove PK Constraint
448 if(oldPKConstraint
!= null) {
449 Constraints
.Remove(oldPKConstraint
);
453 //Does constraint exist for these columns
454 UniqueConstraint uc
= UniqueConstraint
.GetUniqueConstraintForColumnSet(
455 this.Constraints
, (DataColumn
[]) value);
457 //if constraint doesn't exist for columns
458 //create new unique primary key constraint
460 foreach (DataColumn Col
in (DataColumn
[]) value) {
462 if (Col
.Table
== null)
465 if (Columns
.IndexOf (Col
) < 0)
466 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
470 uc
= new UniqueConstraint( (DataColumn
[]) value, true);
472 Constraints
.Add (uc
);
475 //set existing constraint as the new primary key
476 UniqueConstraint
.SetAsPrimaryKey(this.Constraints
, uc
);
479 // if we really supplied some columns fo primary key -
480 // rebuild indexes fo all foreign key constraints
481 if(value.Length
> 0) {
482 foreach(ForeignKeyConstraint constraint
in this.Constraints
.ForeignKeyConstraints
){
483 constraint
.AssertConstraint();
488 // if primary key is null now - drop all the indexes for foreign key constraints
489 foreach(ForeignKeyConstraint constraint
in this.Constraints
.ForeignKeyConstraints
){
490 Index index
= constraint
.Index
;
491 constraint
.Index
= null;
492 this.DropIndex(index
);
500 /// Gets the collection of_rows that belong to this table.
503 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
504 public DataRowCollection Rows
{
505 get { return _rows; }
509 /// Gets or sets an System.ComponentModel.ISite
510 /// for the DataTable.
513 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
514 public override ISite Site
{
515 get { return _site; }
516 set { _site = value; }
520 /// Gets or sets the name of the the DataTable.
522 [DataCategory ("Data")]
523 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
525 [RefreshProperties (RefreshProperties
.All
)]
526 public string TableName
{
527 get { return _tableName == null ? "" : _tableName; }
528 set { _tableName = value; }
531 bool IListSource
.ContainsListCollection
{
533 // the collection is a DataView
538 internal RecordCache RecordCache
{
544 private bool EnforceConstraints
{
545 get { return enforceConstraints; }
547 if (value != enforceConstraints
) {
549 // first assert all unique constraints
550 foreach (UniqueConstraint uc
in this.Constraints
.UniqueConstraints
)
551 uc
.AssertConstraint ();
552 // then assert all foreign keys
553 foreach (ForeignKeyConstraint fk
in this.Constraints
.ForeignKeyConstraints
)
554 fk
.AssertConstraint ();
556 enforceConstraints
= value;
561 internal bool RowsExist(DataColumn
[] columns
, DataColumn
[] relatedColumns
,DataRow row
)
563 int curIndex
= row
.IndexFromVersion(DataRowVersion
.Default
);
564 int tmpRecord
= RecordCache
.NewRecord();
567 for (int i
= 0; i
< relatedColumns
.Length
; i
++) {
568 // according to MSDN: the DataType value for both columns must be identical.
569 columns
[i
].DataContainer
.CopyValue(relatedColumns
[i
].DataContainer
, curIndex
, tmpRecord
);
572 return RowsExist(columns
, tmpRecord
, relatedColumns
.Length
);
575 RecordCache
.DisposeRecord(tmpRecord
);
579 bool RowsExist(DataColumn
[] columns
, int index
, int length
)
581 bool rowsExist
= false;
582 Index indx
= this.GetIndexByColumns (columns
);
584 if (indx
!= null) { // lookup for a row in index
585 rowsExist
= (indx
.FindSimple (index
, length
, false) != null);
588 if(indx
== null || rowsExist
== false) {
589 // no index or rowExist= false, we have to perform full-table scan
590 // check that there is a parent for this row.
591 foreach (DataRow thisRow
in this.Rows
) {
592 if (thisRow
.RowState
!= DataRowState
.Deleted
) {
594 // check if the values in the columns are equal
595 int thisIndex
= thisRow
.IndexFromVersion(DataRowVersion
.Current
);
596 foreach (DataColumn column
in columns
) {
597 if (column
.DataContainer
.CompareValues(thisIndex
, index
) != 0) {
602 if (match
) {// there is a row with columns values equals to those supplied.
613 /// Commits all the changes made to this table since the
614 /// last time AcceptChanges was called.
616 public void AcceptChanges ()
618 //FIXME: Do we need to validate anything here or
619 //try to catch any errors to deal with them?
621 // we do not use foreach because if one of the rows is in Delete state
622 // it will be romeved from Rows and we get an exception.
624 for (int i
= 0; i
< Rows
.Count
; )
627 myRow
.AcceptChanges();
629 // if the row state is Detached it meens that it was removed from row list (Rows)
630 // so we should not increase 'i'.
631 if (myRow
.RowState
!= DataRowState
.Detached
)
637 /// Begins the initialization of a DataTable that is used
638 /// on a form or used by another component. The initialization
639 /// occurs at runtime.
641 public virtual void BeginInit ()
643 fInitInProgress
= true;
647 /// Turns off notifications, index maintenance, and
648 /// constraints while loading data.
651 public void BeginLoadData ()
653 if (!this._duringDataLoad
)
655 //duringDataLoad is important to EndLoadData and
656 //for not throwing unexpected exceptions.
657 this._duringDataLoad
= true;
658 this._nullConstraintViolationDuringDataLoad
= false;
660 if (this.dataSet
!= null)
662 //Saving old Enforce constraints state for later
663 //use in the EndLoadData.
664 this.dataSetPrevEnforceConstraints
= this.dataSet
.EnforceConstraints
;
665 this.dataSet
.EnforceConstraints
= false;
668 //if table does not belong to any data set use EnforceConstraints of the table
669 this.dataTablePrevEnforceConstraints
= this.EnforceConstraints
;
670 this.EnforceConstraints
= false;
677 /// Clears the DataTable of all data.
679 public void Clear () {
680 // Foriegn key constraints are checked in _rows.Clear method
685 /// Clones the structure of the DataTable, including
686 /// all DataTable schemas and constraints.
688 public virtual DataTable
Clone ()
690 // Use Activator so we can use non-public constructors.
691 DataTable Copy
= (DataTable
) Activator
.CreateInstance(GetType(), true);
692 CopyProperties (Copy
);
697 /// Computes the given expression on the current_rows that
698 /// pass the filter criteria.
701 public object Compute (string expression
, string filter
)
703 // expression is an aggregate function
704 // filter is an expression used to limit rows
706 DataRow
[] rows
= Select(filter
);
708 Parser parser
= new Parser (rows
);
709 IExpression expr
= parser
.Compile (expression
);
710 object obj
= expr
.Eval (rows
[0]);
716 /// Copies both the structure and data for this DataTable.
718 public DataTable
Copy ()
720 DataTable copy
= Clone();
722 copy
._duringDataLoad
= true;
723 foreach (DataRow row
in Rows
) {
724 DataRow newRow
= copy
.NewNotInitializedRow();
725 copy
.Rows
.Add(newRow
);
728 copy
._duringDataLoad
= false;
732 internal void CopyRow(DataRow fromRow
,DataRow toRow
)
734 fromRow
.CopyState(toRow
);
736 if (fromRow
.HasVersion(DataRowVersion
.Original
)) {
737 toRow
._original
= toRow
.Table
.RecordCache
.CopyRecord(this,fromRow
._original
,-1);
740 if (fromRow
.HasVersion(DataRowVersion
.Current
)) {
741 toRow
._current
= toRow
.Table
.RecordCache
.CopyRecord(this,fromRow
._current
,-1);
745 private void CopyProperties (DataTable Copy
)
748 Copy
.CaseSensitive
= CaseSensitive
;
749 Copy
.VirginCaseSensitive
= VirginCaseSensitive
;
751 // Copy.ChildRelations
756 Copy
.DisplayExpression
= DisplayExpression
;
757 if(ExtendedProperties
.Count
> 0) {
758 // Cannot copy extended properties directly as the property does not have a set accessor
759 Array tgtArray
= Array
.CreateInstance( typeof (object), ExtendedProperties
.Count
);
760 ExtendedProperties
.Keys
.CopyTo (tgtArray
, 0);
761 for (int i
=0; i
< ExtendedProperties
.Count
; i
++)
762 Copy
.ExtendedProperties
.Add (tgtArray
.GetValue (i
), ExtendedProperties
[tgtArray
.GetValue (i
)]);
764 Copy
.Locale
= Locale
;
765 Copy
.MinimumCapacity
= MinimumCapacity
;
766 Copy
.Namespace
= Namespace
;
767 // Copy.ParentRelations
768 Copy
.Prefix
= Prefix
;
770 Copy
.TableName
= TableName
;
775 foreach (DataColumn Column
in Columns
) {
776 // When cloning a table, the columns may be added in the default
778 if (!Copy
.Columns
.Contains(Column
.ColumnName
)) Copy
.Columns
.Add (CopyColumn (Column
));
781 CopyConstraints(Copy
);
782 // add primary key to the copy
783 if (PrimaryKey
.Length
> 0)
785 DataColumn
[] pColumns
= new DataColumn
[PrimaryKey
.Length
];
786 for (int i
= 0; i
< pColumns
.Length
; i
++)
787 pColumns
[i
] = Copy
.Columns
[PrimaryKey
[i
].ColumnName
];
789 Copy
.PrimaryKey
= pColumns
;
793 private void CopyConstraints(DataTable copy
)
795 UniqueConstraint origUc
;
796 UniqueConstraint copyUc
;
797 for (int i
= 0; i
< this.Constraints
.Count
; i
++)
799 if (this.Constraints
[i
] is UniqueConstraint
)
801 origUc
= (UniqueConstraint
)this.Constraints
[i
];
802 DataColumn
[] columns
= new DataColumn
[origUc
.Columns
.Length
];
803 for (int j
= 0; j
< columns
.Length
; j
++)
804 columns
[j
] = copy
.Columns
[origUc
.Columns
[j
].ColumnName
];
806 copyUc
= new UniqueConstraint(origUc
.ConstraintName
, columns
, origUc
.IsPrimaryKey
);
807 copy
.Constraints
.Add(copyUc
);
812 /// Ends the initialization of a DataTable that is used
813 /// on a form or used by another component. The
814 /// initialization occurs at runtime.
817 public virtual void EndInit ()
819 fInitInProgress
= false;
820 // Add the constraints
821 PostEndInit _postEndInit
= new PostEndInit (_constraintCollection
.PostEndInit
);
826 /// Turns on notifications, index maintenance, and
827 /// constraints after loading data.
829 public void EndLoadData()
832 if (this._duringDataLoad
)
835 if (this.dataSet
!=null)
837 //Getting back to previous EnforceConstraint state
838 this.dataSet
.EnforceConstraints
= this.dataSetPrevEnforceConstraints
;
841 //Getting back to the table's previous EnforceConstraint state
842 this.EnforceConstraints
= this.dataTablePrevEnforceConstraints
;
845 if(this._nullConstraintViolationDuringDataLoad
) {
846 this._nullConstraintViolationDuringDataLoad
= false;
847 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
850 //Returning from loading mode, raising exceptions as usual
851 this._duringDataLoad
= false;
858 /// Gets a copy of the DataTable that contains all
859 /// changes made to it since it was loaded or
860 /// AcceptChanges was last called.
862 public DataTable
GetChanges()
864 return GetChanges(DataRowState
.Added
| DataRowState
.Deleted
| DataRowState
.Modified
);
868 /// Gets a copy of the DataTable containing all
869 /// changes made to it since it was last loaded, or
870 /// since AcceptChanges was called, filtered by DataRowState.
872 public DataTable
GetChanges(DataRowState rowStates
)
874 DataTable copyTable
= null;
876 IEnumerator rowEnumerator
= Rows
.GetEnumerator();
877 while (rowEnumerator
.MoveNext()) {
878 DataRow row
= (DataRow
)rowEnumerator
.Current
;
879 // The spec says relationship constraints may cause Unchanged parent rows to be included but
880 // MS .NET 1.1 does not include Unchanged rows even if their child rows are changed.
881 if (row
.IsRowChanged(rowStates
)) {
882 if (copyTable
== null)
884 DataRow newRow
= copyTable
.NewRow();
885 row
.CopyValuesToRow(newRow
);
886 copyTable
.Rows
.Add (newRow
);
895 public DataTableReader
GetDataReader ()
897 throw new NotImplementedException ();
902 /// Gets an array of DataRow objects that contain errors.
904 public DataRow
[] GetErrors ()
906 ArrayList errors
= new ArrayList();
907 for (int i
= 0; i
< _rows
.Count
; i
++)
909 if (_rows
[i
].HasErrors
)
910 errors
.Add(_rows
[i
]);
913 return (DataRow
[]) errors
.ToArray(typeof(DataRow
));
917 /// This member is only meant to support Mono's infrastructure
919 protected virtual DataTable
CreateInstance ()
921 return Activator
.CreateInstance (this.GetType (), true) as DataTable
;
925 /// This member is only meant to support Mono's infrastructure
927 protected virtual Type
GetRowType ()
929 return typeof (DataRow
);
933 /// This member is only meant to support Mono's infrastructure
935 /// Used for Data Binding between System.Web.UI. controls
938 /// System.Windows.Forms controls like a DataGrid
940 IList IListSource
.GetList ()
942 IList list
= (IList
) _defaultView
;
947 /// Copies a DataRow into a DataTable, preserving any
948 /// property settings, as well as original and current values.
950 public void ImportRow (DataRow row
)
952 DataRow newRow
= NewRow();
954 row
.CopyValuesToRow(newRow
);
958 internal int DefaultValuesRowIndex
961 return _defaultValuesRowIndex
;
966 /// This member is only meant to support Mono's infrastructure
968 void ISerializable
.GetObjectData (SerializationInfo info
, StreamingContext context
)
974 dset
= new DataSet ("tmpDataSet");
975 dset
.Tables
.Add (this);
978 StringWriter sw
= new StringWriter ();
979 XmlTextWriter tw
= new XmlTextWriter (sw
);
980 dset
.WriteIndividualTableContent (tw
, this, XmlWriteMode
.DiffGram
);
983 StringWriter sw2
= new StringWriter ();
984 DataTableCollection tables
= new DataTableCollection (dset
);
986 XmlSchema schema
= dset
.BuildSchema (tables
, null);
990 info
.AddValue ("XmlSchema", sw2
.ToString(), typeof(string));
991 info
.AddValue ("XmlDiffGram", sw
.ToString(), typeof(string));
996 public void Load (IDataReader reader
)
998 throw new NotImplementedException ();
1002 public void Load (IDataReader reader
, LoadOption loadOption
)
1004 throw new NotImplementedException ();
1009 /// Finds and updates a specific row. If no matching row
1010 /// is found, a new row is created using the given values.
1012 public DataRow
LoadDataRow (object[] values
, bool fAcceptChanges
)
1015 if (PrimaryKey
.Length
== 0) {
1016 row
= Rows
.Add (values
);
1018 row
.AcceptChanges ();
1021 bool hasPrimaryValues
= true;
1022 // initiate an array that has the values of the primary keys.
1023 object[] keyValues
= new object[PrimaryKey
.Length
];
1024 for (int i
= 0; i
< keyValues
.Length
&& hasPrimaryValues
; i
++)
1026 if(PrimaryKey
[i
].Ordinal
< values
.Length
)
1027 keyValues
[i
] = values
[PrimaryKey
[i
].Ordinal
];
1029 hasPrimaryValues
= false;
1032 if (hasPrimaryValues
){
1033 // find the row in the table.
1034 row
= Rows
.Find(keyValues
);
1038 row
= Rows
.Add (values
);
1040 row
.ItemArray
= values
;
1043 row
.AcceptChanges ();
1049 internal DataRow
LoadDataRow(IDataRecord record
, int[] mapping
, bool fAcceptChanges
)
1052 if (PrimaryKey
.Length
== 0) {
1054 row
.SetValuesFromDataRecord(record
, mapping
);
1057 if (fAcceptChanges
) {
1058 row
.AcceptChanges();
1062 bool hasPrimaryValues
= true;
1063 int tmpRecord
= this.RecordCache
.NewRecord();
1065 for (int i
= 0; i
< PrimaryKey
.Length
&& hasPrimaryValues
; i
++) {
1066 DataColumn primaryKeyColumn
= PrimaryKey
[i
];
1067 int ordinal
= primaryKeyColumn
.Ordinal
;
1068 if(ordinal
< mapping
.Length
) {
1069 primaryKeyColumn
.DataContainer
.SetItemFromDataRecord(tmpRecord
,record
,mapping
[ordinal
]);
1072 hasPrimaryValues
= false;
1076 if (hasPrimaryValues
) {
1077 // find the row in the table.
1078 row
= Rows
.Find(tmpRecord
,PrimaryKey
.Length
);
1082 this.RecordCache
.DisposeRecord(tmpRecord
);
1087 row
.SetValuesFromDataRecord(record
, mapping
);
1091 row
.SetValuesFromDataRecord(record
, mapping
);
1094 if (fAcceptChanges
) {
1095 row
.AcceptChanges();
1103 public DataRow
LoadDataRow (object[] values
, LoadOption loadOption
)
1105 throw new NotImplementedException ();
1109 public void Merge (DataTable table
)
1111 throw new NotImplementedException ();
1115 public void Merge (DataTable table
, bool preserveChanges
)
1117 throw new NotImplementedException ();
1121 public void Merge (DataTable table
, bool preserveChanges
, MissingSchemaAction missingSchemaAction
)
1123 throw new NotImplementedException ();
1128 /// Creates a new DataRow with the same schema as the table.
1130 public DataRow
NewRow ()
1132 // initiate only one row builder.
1133 if (_rowBuilder
== null)
1134 _rowBuilder
= new DataRowBuilder (this, 0, 0);
1136 // new row get id -1.
1137 _rowBuilder
._rowId
= -1;
1139 // initialize default values row for the first time
1140 if ( _defaultValuesRowIndex
== -1 ) {
1141 _defaultValuesRowIndex
= RecordCache
.NewRecord();
1142 foreach(DataColumn column
in Columns
) {
1143 column
.DataContainer
[_defaultValuesRowIndex
] = column
.DefaultValue
;
1147 return this.NewRowFromBuilder (_rowBuilder
);
1151 /// This member supports the .NET Framework infrastructure
1152 /// and is not intended to be used directly from your code.
1154 protected internal DataRow
[] NewRowArray (int size
)
1156 return (DataRow
[]) Array
.CreateInstance (GetRowType (), size
);
1160 /// Creates a new row from an existing row.
1162 protected virtual DataRow
NewRowFromBuilder (DataRowBuilder builder
)
1164 return new DataRow (builder
);
1167 internal DataRow
NewNotInitializedRow()
1169 return new DataRow(this,-1);
1174 XmlReadMode
ReadXml (Stream stream
)
1176 throw new NotImplementedException ();
1179 public void ReadXmlSchema (Stream stream
)
1181 XmlSchemaMapper mapper
= new XmlSchemaMapper (this);
1182 mapper
.Read (new XmlTextReader(stream
));
1185 public void ReadXmlSchema (TextReader reader
)
1187 XmlSchemaMapper mapper
= new XmlSchemaMapper (this);
1188 mapper
.Read (new XmlTextReader(reader
));
1191 public void ReadXmlSchema (string fileName
)
1193 StreamReader reader
= new StreamReader (fileName
);
1194 ReadXmlSchema (reader
);
1198 public void ReadXmlSchema (XmlReader reader
)
1200 XmlSchemaMapper mapper
= new XmlSchemaMapper (this);
1201 mapper
.Read (reader
);
1206 /// Rolls back all changes that have been made to the
1207 /// table since it was loaded, or the last time AcceptChanges
1210 public void RejectChanges ()
1212 for (int i
= _rows
.Count
- 1; i
>= 0; i
--) {
1213 DataRow row
= _rows
[i
];
1214 if (row
.RowState
!= DataRowState
.Unchanged
)
1215 _rows
[i
].RejectChanges ();
1220 /// Resets the DataTable to its original state.
1222 public virtual void Reset ()
1225 while (ParentRelations
.Count
> 0)
1227 if (dataSet
.Relations
.Contains(ParentRelations
[ParentRelations
.Count
- 1].RelationName
))
1228 dataSet
.Relations
.Remove(ParentRelations
[ParentRelations
.Count
- 1]);
1231 while (ChildRelations
.Count
> 0)
1233 if (dataSet
.Relations
.Contains(ChildRelations
[ChildRelations
.Count
- 1].RelationName
))
1234 dataSet
.Relations
.Remove(ChildRelations
[ChildRelations
.Count
- 1]);
1236 Constraints
.Clear();
1241 /// Gets an array of all DataRow objects.
1243 public DataRow
[] Select ()
1245 return Select(String
.Empty
, String
.Empty
, DataViewRowState
.CurrentRows
);
1249 /// Gets an array of all DataRow objects that match
1250 /// the filter criteria in order of primary key (or
1251 /// lacking one, order of addition.)
1253 public DataRow
[] Select (string filterExpression
)
1255 return Select(filterExpression
, String
.Empty
, DataViewRowState
.CurrentRows
);
1259 /// Gets an array of all DataRow objects that
1260 /// match the filter criteria, in the the
1261 /// specified sort order.
1263 public DataRow
[] Select (string filterExpression
, string sort
)
1265 return Select(filterExpression
, sort
, DataViewRowState
.CurrentRows
);
1269 /// Gets an array of all DataRow objects that match
1270 /// the filter in the order of the sort, that match
1271 /// the specified state.
1274 public DataRow
[] Select(string filterExpression
, string sort
, DataViewRowState recordStates
)
1276 if (filterExpression
== null)
1277 filterExpression
= String
.Empty
;
1279 IExpression filter
= null;
1280 if (filterExpression
!= String
.Empty
) {
1281 Parser parser
= new Parser ();
1282 filter
= parser
.Compile (filterExpression
);
1285 ArrayList rowList
= new ArrayList();
1286 int recordStateFilter
= GetRowStateFilter(recordStates
);
1287 foreach (DataRow row
in Rows
) {
1288 if (((int)row
.RowState
& recordStateFilter
) != 0) {
1289 if (filter
!= null && !(bool)filter
.Eval (row
))
1295 DataRow
[] dataRows
= (DataRow
[])rowList
.ToArray(typeof(DataRow
));
1297 if (sort
!= null && !sort
.Equals(String
.Empty
))
1299 SortableColumn
[] sortableColumns
= null;
1301 sortableColumns
= ParseTheSortString (sort
);
1302 if (sortableColumns
== null)
1303 throw new Exception ("sort expression result is null");
1304 if (sortableColumns
.Length
== 0)
1305 throw new Exception("sort expression result is 0");
1307 RowSorter rowSorter
= new RowSorter (this, dataRows
, sortableColumns
);
1308 dataRows
= rowSorter
.SortRows ();
1310 sortableColumns
= null;
1318 internal void AddIndex (Index index
)
1320 if (_indexes
== null)
1321 _indexes
= new ArrayList();
1323 _indexes
.Add (index
);
1326 internal void RemoveIndex (Index indx
)
1328 _indexes
.Remove (indx
);
1331 internal Index
GetIndexByColumns (DataColumn
[] columns
)
1333 return GetIndexByColumns(columns
,false,false);
1336 // internal Index GetIndexByColumnsExtended(DataColumn[] columns)
1338 // DataColumn[] pkColumns = this.PrimaryKey;
1339 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1340 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1341 // Array.Copy(columns,0,cols,0,columns.Length);
1342 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1344 // return _getIndexByColumns(cols,false,false);
1350 internal Index
GetIndexByColumns (DataColumn
[] columns
, bool unique
)
1352 return GetIndexByColumns(columns
,unique
,true);
1355 // internal Index GetIndexByColumnsExtended(DataColumn[] columns, bool unique)
1357 // DataColumn[] pkColumns = this.PrimaryKey;
1358 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1359 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1360 // Array.Copy(columns,0,cols,0,columns.Length);
1361 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1363 // return _getIndexByColumns(cols,unique,true);
1369 internal Index
GetIndexByColumns(DataColumn
[] columns
, bool unique
, bool useUnique
)
1371 if (_indexes
!= null) {
1372 foreach (Index indx
in _indexes
) {
1374 if ((!useUnique
) || ((useUnique
)&& (indx
.IsUnique
))) {
1375 found
= DataColumn
.AreColumnSetsTheSame (indx
.Columns
, columns
);
1385 internal void DeleteRowFromIndexes (DataRow row
)
1387 if (_indexes
!= null) {
1388 foreach (Index indx
in _indexes
) {
1394 private static int GetRowStateFilter(DataViewRowState recordStates
)
1398 if ((recordStates
& DataViewRowState
.Added
) != 0)
1399 flag
|= (int)DataRowState
.Added
;
1400 if ((recordStates
& DataViewRowState
.Deleted
) != 0)
1401 flag
|= (int)DataRowState
.Deleted
;
1402 if ((recordStates
& DataViewRowState
.ModifiedCurrent
) != 0)
1403 flag
|= (int)DataRowState
.Modified
;
1404 if ((recordStates
& DataViewRowState
.ModifiedOriginal
) != 0)
1405 flag
|= (int)DataRowState
.Modified
;
1406 if ((recordStates
& DataViewRowState
.Unchanged
) != 0)
1407 flag
|= (int)DataRowState
.Unchanged
;
1413 /// Gets the TableName and DisplayExpression, if
1414 /// there is one as a concatenated string.
1416 public override string ToString()
1418 //LAMESPEC: spec says concat the two. impl puts a
1419 //plus sign infront of DisplayExpression
1420 string retVal
= TableName
;
1421 if(DisplayExpression
!= null && DisplayExpression
!= "")
1422 retVal
+= " + " + DisplayExpression
;
1428 public void WriteXml (Stream stream
)
1430 throw new NotImplementedException ();
1434 public void WriteXml (TextWriter writer
)
1436 throw new NotImplementedException ();
1440 public void WriteXml (XmlWriter writer
)
1442 throw new NotImplementedException ();
1446 public void WriteXml (string fileName
)
1448 throw new NotImplementedException ();
1452 public void WriteXml (Stream stream
, XmlWriteMode mode
)
1454 throw new NotImplementedException ();
1458 public void WriteXml (TextWriter writer
, XmlWriteMode mode
)
1460 throw new NotImplementedException ();
1464 public void WriteXml (XmlWriter writer
, XmlWriteMode mode
)
1466 throw new NotImplementedException ();
1470 public void WriteXml (string fileName
, XmlWriteMode mode
)
1472 throw new NotImplementedException ();
1476 public void WriteXmlSchema (Stream stream
)
1478 throw new NotImplementedException ();
1482 public void WriteXmlSchema (TextWriter writer
)
1484 throw new NotImplementedException ();
1488 public void WriteXmlSchema (XmlWriter writer
)
1490 throw new NotImplementedException ();
1494 public void WriteXmlSchema (string fileName
)
1496 throw new NotImplementedException ();
1503 /// Raises the ColumnChanged event.
1505 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e
) {
1506 if (null != ColumnChanged
) {
1507 ColumnChanged (this, e
);
1511 internal void RaiseOnColumnChanged (DataColumnChangeEventArgs e
) {
1516 /// Raises the ColumnChanging event.
1518 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e
) {
1519 if (null != ColumnChanging
) {
1520 ColumnChanging (this, e
);
1525 /// Raises the PropertyChanging event.
1528 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent
) {
1529 // if (null != PropertyChanging)
1531 // PropertyChanging (this, e);
1536 /// Notifies the DataTable that a DataColumn is being removed.
1539 protected internal virtual void OnRemoveColumn (DataColumn column
) {
1544 /// Raises the RowChanged event.
1546 protected virtual void OnRowChanged (DataRowChangeEventArgs e
) {
1547 if (null != RowChanged
) {
1548 RowChanged(this, e
);
1554 /// Raises the RowChanging event.
1556 protected virtual void OnRowChanging (DataRowChangeEventArgs e
) {
1557 if (null != RowChanging
) {
1558 RowChanging(this, e
);
1563 /// Raises the RowDeleted event.
1565 protected virtual void OnRowDeleted (DataRowChangeEventArgs e
) {
1566 if (null != RowDeleted
) {
1567 RowDeleted(this, e
);
1572 /// Raises the RowDeleting event.
1574 protected virtual void OnRowDeleting (DataRowChangeEventArgs e
) {
1575 if (null != RowDeleting
) {
1576 RowDeleting(this, e
);
1581 private DataColumn
CopyColumn (DataColumn Column
) {
1582 DataColumn Copy
= new DataColumn ();
1584 // Copy all the properties of column
1585 Copy
.AllowDBNull
= Column
.AllowDBNull
;
1586 Copy
.AutoIncrement
= Column
.AutoIncrement
;
1587 Copy
.AutoIncrementSeed
= Column
.AutoIncrementSeed
;
1588 Copy
.AutoIncrementStep
= Column
.AutoIncrementStep
;
1589 Copy
.Caption
= Column
.Caption
;
1590 Copy
.ColumnMapping
= Column
.ColumnMapping
;
1591 Copy
.ColumnName
= Column
.ColumnName
;
1593 Copy
.DataType
= Column
.DataType
;
1594 Copy
.DefaultValue
= Column
.DefaultValue
;
1595 Copy
.Expression
= Column
.Expression
;
1596 //Copy.ExtendedProperties
1597 Copy
.MaxLength
= Column
.MaxLength
;
1598 Copy
.Namespace
= Column
.Namespace
;
1599 Copy
.Prefix
= Column
.Prefix
;
1600 Copy
.ReadOnly
= Column
.ReadOnly
;
1602 //we do not copy the unique value - it will be copyied when copying the constraints.
1603 //Copy.Unique = Column.Unique;
1609 /// Occurs when after a value has been changed for
1610 /// the specified DataColumn in a DataRow.
1612 [DataCategory ("Data")]
1613 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1614 public event DataColumnChangeEventHandler ColumnChanged
;
1617 /// Occurs when a value is being changed for the specified
1618 /// DataColumn in a DataRow.
1620 [DataCategory ("Data")]
1621 [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.")]
1622 public event DataColumnChangeEventHandler ColumnChanging
;
1625 /// Occurs after a DataRow has been changed successfully.
1627 [DataCategory ("Data")]
1628 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1629 public event DataRowChangeEventHandler RowChanged
;
1632 /// Occurs when a DataRow is changing.
1634 [DataCategory ("Data")]
1635 [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.")]
1636 public event DataRowChangeEventHandler RowChanging
;
1639 /// Occurs after a row in the table has been deleted.
1641 [DataCategory ("Data")]
1642 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
1643 public event DataRowChangeEventHandler RowDeleted
;
1646 /// Occurs before a row in the table is about to be deleted.
1648 [DataCategory ("Data")]
1649 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1650 public event DataRowChangeEventHandler RowDeleting
;
1652 #endregion // Events
1655 /// Removes all UniqueConstraints
1657 private void RemoveUniqueConstraints ()
1659 foreach (Constraint Cons
in Constraints
) {
1661 if (Cons
is UniqueConstraint
) {
1662 Constraints
.Remove (Cons
);
1667 UniqueConstraint
.SetAsPrimaryKey(this.Constraints
, null);
1670 // to parse the sort string for DataTable:Select(expression,sort)
1671 // into sortable columns (think ORDER BY,
1672 // such as, "customer ASC, price DESC" )
1673 internal SortableColumn
[] ParseTheSortString (string sort
)
1675 SortableColumn
[] sortColumns
= null;
1676 ArrayList columns
= null;
1678 if (sort
!= null && !sort
.Equals ("")) {
1679 columns
= new ArrayList ();
1680 string[] columnExpression
= sort
.Trim ().Split (new char[1] {','}
);
1682 for (int c
= 0; c
< columnExpression
.Length
; c
++) {
1683 string[] columnSortInfo
= columnExpression
[c
].Trim ().Split (new char[1] {' '}
);
1685 string columnName
= columnSortInfo
[0].Trim ();
1686 string sortOrder
= "ASC";
1687 if (columnSortInfo
.Length
> 1)
1688 sortOrder
= columnSortInfo
[1].Trim ().ToUpper (Locale
);
1690 ListSortDirection sortDirection
= ListSortDirection
.Ascending
;
1691 switch (sortOrder
) {
1693 sortDirection
= ListSortDirection
.Ascending
;
1696 sortDirection
= ListSortDirection
.Descending
;
1699 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression
[c
]);
1703 ord
= Int32
.Parse (columnName
);
1705 catch (FormatException
) {
1708 DataColumn dc
= null;
1710 dc
= _columnCollection
[columnName
];
1712 dc
= _columnCollection
[ord
];
1713 SortableColumn sortCol
= new SortableColumn (dc
,sortDirection
);
1714 columns
.Add (sortCol
);
1716 sortColumns
= (SortableColumn
[]) columns
.ToArray (typeof (SortableColumn
));
1721 internal class SortableColumn
1723 private DataColumn col
;
1724 private ListSortDirection dir
;
1726 internal SortableColumn (DataColumn column
,
1727 ListSortDirection direction
)
1733 public DataColumn Column
{
1739 public ListSortDirection SortDirection
{
1746 private class RowSorter
: IComparer
1748 private DataTable table
;
1749 private SortableColumn
[] sortColumns
;
1750 private DataRow
[] rowsToSort
;
1752 internal RowSorter(DataTable table
,
1753 DataRow
[] unsortedRows
,
1754 SortableColumn
[] sortColumns
)
1757 this.sortColumns
= sortColumns
;
1758 this.rowsToSort
= unsortedRows
;
1761 public SortableColumn
[] SortColumns
{
1767 public DataRow
[] SortRows ()
1769 Array
.Sort (rowsToSort
, this);
1773 int IComparer
.Compare (object x
, object y
)
1776 throw new Exception ("Object to compare is null: x");
1778 throw new Exception ("Object to compare is null: y");
1780 throw new Exception ("Object to compare is not DataRow: x is " + x
.GetType().ToString());
1782 throw new Exception ("Object to compare is not DataRow: y is " + x
.GetType().ToString());
1784 DataRow rowx
= (DataRow
) x
;
1785 DataRow rowy
= (DataRow
) y
;
1787 for(int i
= 0; i
< sortColumns
.Length
; i
++) {
1788 SortableColumn sortColumn
= sortColumns
[i
];
1789 DataColumn dc
= sortColumn
.Column
;
1791 int result
= dc
.CompareValues(
1792 rowx
.IndexFromVersion(DataRowVersion
.Default
),
1793 rowy
.IndexFromVersion(DataRowVersion
.Default
));
1796 if (sortColumn
.SortDirection
== ListSortDirection
.Ascending
) {
1809 /// Creates new index for a table
1811 internal Index
CreateIndex(string name
, DataColumn
[] columns
, bool unique
)
1813 // first check whenever index exists on the columns
1814 Index idx
= this.GetIndexByColumns(columns
);
1816 // if index on this columns already exists - return it
1821 Index newIndex
= new Index(name
,this,columns
,unique
);
1823 //InitializeIndex (newIndex);
1825 // add new index to table indexes
1826 this.AddIndex(newIndex
);
1831 // /// Creates new extended index for a table
1833 // internal Index CreateIndexExtended(string name, DataColumn[] columns, bool unique)
1835 // // first check whenever extended index exists on the columns
1836 // Index idx = this.GetIndexByColumnsExtended(columns);
1837 // if(idx != null) {
1838 // // if extended index on this columns already exists - return it
1842 // DataColumn[] pkColumns = this.PrimaryKey;
1843 // if((pkColumns != null) && (pkColumns.Length > 0)) {
1844 // DataColumn[] cols = new DataColumn[columns.Length + pkColumns.Length];
1845 // Array.Copy(columns,0,cols,0,columns.Length);
1846 // Array.Copy(pkColumns,0,cols,columns.Length,pkColumns.Length);
1847 // return this.CreateIndex(name, cols, unique);
1850 // throw new InvalidOperationException("Can not create extended index if the primary key is null or primary key does not contains any row");
1855 // /// Drops extended index if it is not referenced anymore
1856 // /// by any of table constraints
1858 // internal void DropIndexExtended(DataColumn[] columns)
1860 // // first check whenever extended index exists on the columns
1861 // Index index = this.GetIndexByColumnsExtended(columns);
1862 // if(index == null) {
1863 // // if no extended index on this columns exists - do nothing
1866 // this.DropIndex(index);
1870 /// Drops index specified by columns if it is not referenced anymore
1871 /// by any of table constraints
1873 internal void DropIndex(DataColumn
[] columns
)
1875 // first check whenever index exists for the columns
1876 Index index
= this.GetIndexByColumns(columns
);
1878 // if no index on this columns already exists - do nothing
1881 this.DropIndex(index
);
1884 internal void DropIndex(Index index
)
1886 // loop through table constraints and checks
1887 foreach(Constraint constraint
in Constraints
) {
1888 // if we found another reference to the index we do not remove the index.
1889 if (index
== constraint
.Index
)
1893 this.RemoveIndex(index
);
1896 internal void InitializeIndex (Index indx
)
1898 DataRow
[] rows
= new DataRow
[this.Rows
.Count
];
1899 this.Rows
.CopyTo (rows
, 0);
1901 // fill index with table rows
1902 foreach(DataRow row
in this.Rows
) {
1903 if(row
.RowState
!= DataRowState
.Deleted
) {
1904 indx
.Insert(new Node(row
), DataRowVersion
.Default
);