2 // System.Data.DataRow.cs
5 // Rodrigo Moya <rodrigo@ximian.com>
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Tim Coleman <tim@timcoleman.com>
8 // Ville Palo <vi64pa@koti.soon.fi>
9 // Alan Tam Siu Lung <Tam@SiuLung.com>
11 // (C) Ximian, Inc 2002
12 // (C) Daniel Morgan 2002, 2003
13 // Copyright (C) 2002 Tim Coleman
17 using System.Collections;
18 using System.Globalization;
20 namespace System.Data {
22 /// Represents a row of data in a DataTable.
29 private DataTable _table;
31 private object[] original;
32 private object[] proposed;
33 private object[] current;
35 private string[] columnErrors;
36 private string rowError;
37 private DataRowState rowState;
38 internal int xmlRowID = 0;
39 internal bool _nullConstraintViolation;
40 private string _nullConstraintMessage;
41 private bool editing = false;
42 private bool _hasParentCollection;
43 private bool _inChangingEvent;
51 /// This member supports the .NET Framework infrastructure and is not intended to be
52 /// used directly from your code.
54 protected internal DataRow (DataRowBuilder builder)
56 _table = builder.Table;
57 // Get the row id from the builder.
58 _rowId = builder._rowId;
62 proposed = new object[_table.Columns.Count];
63 // Initialise the data coloumns of the row with the dafault values, if any
64 for (int c = 0; c < _table.Columns.Count; c++)
66 if(_table.Columns [c].DefaultValue == null)
67 proposed[c] = DBNull.Value;
69 proposed [c] = _table.Columns[c].DefaultValue;
72 columnErrors = new string[_table.Columns.Count];
73 rowError = String.Empty;
75 //on first creating a DataRow it is always detached.
76 rowState = DataRowState.Detached;
78 foreach (DataColumn Col in _table.Columns) {
80 if (Col.AutoIncrement) {
81 this [Col] = Col.AutoIncrementValue();
84 _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
93 /// Gets a value indicating whether there are errors in a row.
95 public bool HasErrors {
97 if (RowError != string.Empty)
100 for (int i= 0; i < columnErrors.Length; i++){
101 if (columnErrors[i] != null && columnErrors[i] != string.Empty)
110 /// Gets or sets the data stored in the column specified by name.
112 public object this[string columnName] {
113 get { return this[columnName, DataRowVersion.Default]; }
115 int columnIndex = _table.Columns.IndexOf (columnName);
116 if (columnIndex == -1)
117 throw new IndexOutOfRangeException ();
118 this[columnIndex] = value;
123 /// Gets or sets the data stored in specified DataColumn
125 public object this[DataColumn column] {
128 return this[column, DataRowVersion.Default];}
130 int columnIndex = _table.Columns.IndexOf (column);
131 if (columnIndex == -1)
132 throw new ArgumentException ("The column does not belong to this table.");
133 this[columnIndex] = value;
138 /// Gets or sets the data stored in column specified by index.
140 public object this[int columnIndex] {
141 get { return this[columnIndex, DataRowVersion.Default]; }
143 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
144 throw new IndexOutOfRangeException ();
145 if (rowState == DataRowState.Deleted)
146 throw new DeletedRowInaccessibleException ();
147 DataColumn column = _table.Columns[columnIndex];
148 _table.ChangingDataColumn (this, column, value);
151 bool orginalEditing = editing;
152 if (!orginalEditing) BeginEdit ();
153 object v = SetColumnValue (value, columnIndex);
154 proposed[columnIndex] = v;
155 _table.ChangedDataColumn (this, column, v);
156 if (!orginalEditing) EndEdit ();
161 /// Gets the specified version of data stored in the named column.
163 public object this[string columnName, DataRowVersion version] {
165 int columnIndex = _table.Columns.IndexOf (columnName);
166 if (columnIndex == -1)
167 throw new IndexOutOfRangeException ();
168 return this[columnIndex, version];
173 /// Gets the specified version of data stored in the specified DataColumn.
175 public object this[DataColumn column, DataRowVersion version] {
177 if (column.Table != Table)
178 throw new ArgumentException ("The column does not belong to this table.");
179 int columnIndex = column.Ordinal;
180 return this[columnIndex, version];
185 /// Gets the data stored in the column, specified by index and version of the data to
188 public object this[int columnIndex, DataRowVersion version] {
190 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
191 throw new IndexOutOfRangeException ();
192 // Accessing deleted rows
193 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
194 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
196 DataColumn col = _table.Columns[columnIndex];
197 if (col.Expression != String.Empty) {
198 object o = col.CompiledExpression.Eval (this);
199 return Convert.ChangeType (o, col.DataType);
202 if (HasVersion(version))
206 case DataRowVersion.Default:
207 if (editing || rowState == DataRowState.Detached)
208 return proposed[columnIndex];
209 return current[columnIndex];
210 case DataRowVersion.Proposed:
211 return proposed[columnIndex];
212 case DataRowVersion.Current:
213 return current[columnIndex];
214 case DataRowVersion.Original:
215 return original[columnIndex];
217 throw new ArgumentException ();
220 if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == null)
221 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
223 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
227 internal void SetOriginalValue (string columnName, object val)
229 int columnIndex = _table.Columns.IndexOf (columnName);
230 DataColumn column = _table.Columns[columnIndex];
231 _table.ChangingDataColumn (this, column, val);
233 if (original == null) original = new object [_table.Columns.Count];
234 val = SetColumnValue (val, columnIndex);
235 original[columnIndex] = val;
236 rowState = DataRowState.Modified;
240 /// Gets or sets all of the values for this row through an array.
242 public object[] ItemArray {
245 if (rowState == DataRowState.Detached)
246 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
247 // Accessing deleted rows
248 if (rowState == DataRowState.Deleted)
249 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
254 if (value.Length > _table.Columns.Count)
255 throw new ArgumentException ();
257 if (rowState == DataRowState.Deleted)
258 throw new DeletedRowInaccessibleException ();
260 object[] newItems = new object[_table.Columns.Count];
262 for (int i = 0; i < _table.Columns.Count; i++) {
264 if (i < value.Length)
269 newItems[i] = SetColumnValue (v, i);
272 bool orginalEditing = editing;
273 if (!orginalEditing) BeginEdit ();
275 if (!orginalEditing) EndEdit ();
279 private object SetColumnValue (object v, int index)
281 object newval = null;
282 DataColumn col = _table.Columns[index];
284 if (_hasParentCollection && col.ReadOnly && v != this[index])
285 throw new ReadOnlyException ();
289 if (col.DataType.ToString().Equals("System.Guid"))
290 throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
292 if(col.DefaultValue != DBNull.Value)
294 newval = col.DefaultValue;
296 else if(col.AutoIncrement == true)
298 newval = this [index];
302 if (!col.AllowDBNull)
304 //Constraint violations during data load is raise in DataTable EndLoad
305 this._nullConstraintViolation = true;
306 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
309 newval = DBNull.Value;
312 else if (v == DBNull.Value)
315 if (!col.AllowDBNull)
317 //Constraint violations during data load is raise in DataTable EndLoad
318 this._nullConstraintViolation = true;
319 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
322 newval = DBNull.Value;
326 Type vType = v.GetType(); // data type of value
327 Type cType = col.DataType; // column data type
330 TypeCode typeCode = Type.GetTypeCode(cType);
332 case TypeCode.Boolean :
333 v = Convert.ToBoolean (v);
336 v = Convert.ToByte (v);
339 v = Convert.ToChar (v);
341 case TypeCode.DateTime :
342 v = Convert.ToDateTime (v);
344 case TypeCode.Decimal :
345 v = Convert.ToDecimal (v);
347 case TypeCode.Double :
348 v = Convert.ToDouble (v);
350 case TypeCode.Int16 :
351 v = Convert.ToInt16 (v);
353 case TypeCode.Int32 :
354 v = Convert.ToInt32 (v);
356 case TypeCode.Int64 :
357 v = Convert.ToInt64 (v);
359 case TypeCode.SByte :
360 v = Convert.ToSByte (v);
362 case TypeCode.Single :
363 v = Convert.ToSingle (v);
365 case TypeCode.String :
366 v = Convert.ToString (v);
368 case TypeCode.UInt16 :
369 v = Convert.ToUInt16 (v);
371 case TypeCode.UInt32 :
372 v = Convert.ToUInt32 (v);
374 case TypeCode.UInt64 :
375 v = Convert.ToUInt64 (v);
379 switch(cType.ToString()) {
380 case "System.TimeSpan" :
381 v = (System.TimeSpan) v;
386 case "System.Object" :
387 //v = (System.Object) v;
391 throw new InvalidCastException("Type not supported.");
399 // The MaxLength property is ignored for non-text columns
400 if ((Type.GetTypeCode(vType) == TypeCode.String) && (this.Table.Columns[index].MaxLength != -1) &&
401 (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
402 throw new ArgumentException("Cannot set column '" + this.Table.Columns[index].ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
405 if(col.AutoIncrement == true) {
406 long inc = Convert.ToInt64(v);
407 col.UpdateAutoIncrementValue (inc);
410 col.DataHasBeenSet = true;
415 /// Gets or sets the custom error description for a row.
417 public string RowError {
418 get { return rowError; }
419 set { rowError = value; }
423 /// Gets the current state of the row in regards to its relationship to the
424 /// DataRowCollection.
426 public DataRowState RowState {
427 get { return rowState; }
430 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
431 //to a Datatable so I added this method. Delete if there is a better way.
432 internal void AttachRow() {
435 rowState = DataRowState.Added;
438 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
439 //from a Datatable so I added this method. Delete if there is a better way.
440 internal void DetachRow() {
443 _hasParentCollection = false;
444 rowState = DataRowState.Detached;
448 /// Gets the DataTable for which this row has a schema.
450 public DataTable Table {
451 get { return _table; }
455 /// Gets and sets index of row. This is used from
458 internal int XmlRowID {
459 get { return xmlRowID; }
460 set { xmlRowID = value; }
464 /// Gets and sets index of row.
467 get { return _rowId; }
468 set { _rowId = value; }
476 /// Commits all the changes made to this row since the last time AcceptChanges was
479 public void AcceptChanges ()
481 EndEdit(); // in case it hasn't been called
483 case DataRowState.Added:
484 case DataRowState.Modified:
485 rowState = DataRowState.Unchanged;
487 case DataRowState.Deleted:
488 _table.Rows.RemoveInternal (this);
491 case DataRowState.Detached:
492 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
494 // Accept from detached
495 if (original == null)
496 original = new object[_table.Columns.Count];
497 Array.Copy (current, original, _table.Columns.Count);
501 /// Begins an edit operation on a DataRow object.
504 public void BeginEdit ()
506 if (rowState == DataRowState.Deleted)
507 throw new DeletedRowInaccessibleException ();
508 if (!HasVersion (DataRowVersion.Proposed)) {
509 proposed = new object[_table.Columns.Count];
510 Array.Copy (current, proposed, current.Length);
512 //TODO: Suspend validation
517 /// Cancels the current edit on the row.
520 public void CancelEdit ()
524 if (HasVersion (DataRowVersion.Proposed)) {
526 if (rowState == DataRowState.Modified)
527 rowState = DataRowState.Unchanged;
532 /// Clears the errors for the row, including the RowError and errors set with
535 public void ClearErrors ()
537 rowError = String.Empty;
538 columnErrors = new String[_table.Columns.Count];
542 /// Deletes the DataRow.
545 public void Delete ()
547 _table.DeletingDataRow(this, DataRowAction.Delete);
549 case DataRowState.Added:
550 // check what to do with child rows
551 CheckChildRows(DataRowAction.Delete);
552 _table.DeleteRowFromIndexes (this);
553 Table.Rows.RemoveInternal (this);
555 // if row was in Added state we move it to Detached.
558 case DataRowState.Deleted:
561 // check what to do with child rows
562 CheckChildRows(DataRowAction.Delete);
563 _table.DeleteRowFromIndexes (this);
564 rowState = DataRowState.Deleted;
567 _table.DeletedDataRow(this, DataRowAction.Delete);
570 // check the child rows of this row before deleting the row.
571 private void CheckChildRows(DataRowAction action)
574 // in this method we find the row that this row is in a relation with them.
575 // in shortly we find all child rows of this row.
576 // then we function according to the DeleteRule of the foriegnkey.
579 // 1. find if this row is attached to dataset.
580 // 2. find if EnforceConstraints is true.
581 // 3. find if there are any constraint on the table that the row is in.
582 if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
584 foreach (DataTable table in _table.DataSet.Tables)
586 // loop on all ForeignKeyConstrain of the table.
587 foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
589 if (fk.RelatedTable == _table)
592 if (action == DataRowAction.Delete)
593 rule = fk.DeleteRule;
595 rule = fk.UpdateRule;
596 CheckChildRows(fk, action, rule);
603 private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
605 DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
608 case Rule.Cascade: // delete or change all relted rows.
609 if (childRows != null)
611 for (int j = 0; j < childRows.Length; j++)
613 // if action is delete we delete all child rows
614 if (action == DataRowAction.Delete)
616 if (childRows[j].RowState != DataRowState.Deleted)
617 childRows[j].Delete();
619 // if action is change we change the values in the child row
620 else if (action == DataRowAction.Change)
622 // change only the values in the key columns
623 // set the childcolumn value to the new parent row value
624 for (int k = 0; k < fkc.Columns.Length; k++)
625 childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
630 case Rule.None: // throw an exception if there are any child rows.
631 if (childRows != null)
633 for (int j = 0; j < childRows.Length; j++)
635 if (childRows[j].RowState != DataRowState.Deleted)
637 string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
638 string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
639 string message = action == DataRowAction.Delete ? delStr : changeStr;
640 throw new InvalidConstraintException(message);
645 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
646 if (childRows != null)
648 for (int j = 0; j < childRows.Length; j++)
650 DataRow child = childRows[j];
651 if (childRows[j].RowState != DataRowState.Deleted)
653 //set only the key columns to default
654 for (int k = 0; k < fkc.Columns.Length; k++)
655 child[fkc.Columns[k]] = fkc.Columns[k].DefaultValue;
660 case Rule.SetNull: // set the values in the child row to null.
661 if (childRows != null)
663 for (int j = 0; j < childRows.Length; j++)
665 DataRow child = childRows[j];
666 if (childRows[j].RowState != DataRowState.Deleted)
668 // set only the key columns to DBNull
669 for (int k = 0; k < fkc.Columns.Length; k++)
670 child.SetNull(fkc.Columns[k]);
680 /// Ends the edit occurring on the row.
683 public void EndEdit ()
685 if (_inChangingEvent)
686 throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
687 if (rowState == DataRowState.Detached)
693 if (HasVersion (DataRowVersion.Proposed))
695 _inChangingEvent = true;
698 _table.ChangingDataRow(this, DataRowAction.Change);
702 _inChangingEvent = false;
704 if (rowState == DataRowState.Unchanged)
705 rowState = DataRowState.Modified;
707 //Calling next method validates UniqueConstraints
711 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
712 _table.Rows.ValidateDataRowInternal(this);
721 // Now we are going to check all child rows of current row.
722 // In the case the cascade is true the child rows will look up for
723 // parent row. since lookup in index is always on current,
724 // we have to move proposed version of current row to current
725 // in the case of check child row failure we are rolling
726 // current row state back.
727 object[] backup = current;
730 bool editing_backup = editing;
733 // check all child rows.
734 CheckChildRows(DataRowAction.Change);
736 catch (Exception ex) {
737 // if check child rows failed - rollback to previous state
738 // i.e. restore proposed and current versions
741 editing = editing_backup;
742 // since we failed - propagate an exception
745 _table.ChangedDataRow(this, DataRowAction.Change);
750 /// Gets the child rows of this DataRow using the specified DataRelation.
752 public DataRow[] GetChildRows (DataRelation relation)
754 return GetChildRows (relation, DataRowVersion.Current);
758 /// Gets the child rows of a DataRow using the specified RelationName of a
761 public DataRow[] GetChildRows (string relationName)
763 return GetChildRows (Table.DataSet.Relations[relationName]);
767 /// Gets the child rows of a DataRow using the specified DataRelation, and
770 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version)
772 if (relation == null)
773 return new DataRow[0];
775 if (this.Table == null || RowState == DataRowState.Detached)
776 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
778 if (relation.DataSet != this.Table.DataSet)
779 throw new ArgumentException();
781 if (relation.ChildKeyConstraint != null)
782 return GetChildRows (relation.ChildKeyConstraint, version);
784 ArrayList rows = new ArrayList();
785 DataColumn[] parentColumns = relation.ParentColumns;
786 DataColumn[] childColumns = relation.ChildColumns;
787 int numColumn = parentColumns.Length;
788 if (HasVersion(version))
790 object[] vals = new object[parentColumns.Length];
791 for (int i = 0; i < vals.Length; i++)
792 vals[i] = this[parentColumns[i], version];
794 foreach (DataRow row in relation.ChildTable.Rows)
796 bool allColumnsMatch = false;
797 if (row.HasVersion(DataRowVersion.Default))
799 allColumnsMatch = true;
800 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
802 if (!vals[columnCnt].Equals(
803 row[childColumns[columnCnt], DataRowVersion.Default]))
805 allColumnsMatch = false;
810 if (allColumnsMatch) rows.Add(row);
813 DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
814 rows.CopyTo(result, 0);
819 /// Gets the child rows of a DataRow using the specified RelationName of a
820 /// DataRelation, and DataRowVersion.
822 public DataRow[] GetChildRows (string relationName, DataRowVersion version)
824 return GetChildRows (Table.DataSet.Relations[relationName], version);
827 private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version)
829 ArrayList rows = new ArrayList();
830 DataColumn[] parentColumns = fkc.RelatedColumns;
831 DataColumn[] childColumns = fkc.Columns;
832 int numColumn = parentColumns.Length;
833 if (HasVersion(version))
835 object[] vals = new object[parentColumns.Length];
836 for (int i = 0; i < vals.Length; i++)
837 vals[i] = this[parentColumns[i], version];
839 Index index = fkc.Index;
841 // get the child rows from the index
842 Node[] childNodes = index.FindAllSimple (vals);
843 for (int i = 0; i < childNodes.Length; i++) {
844 rows.Add (childNodes[i].Row);
847 else { // if there is no index we search manualy.
848 foreach (DataRow row in fkc.Table.Rows)
850 bool allColumnsMatch = false;
851 if (row.HasVersion(DataRowVersion.Default))
853 allColumnsMatch = true;
854 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
856 if (!vals[columnCnt].Equals(
857 row[childColumns[columnCnt], DataRowVersion.Default]))
859 allColumnsMatch = false;
864 if (allColumnsMatch) rows.Add(row);
869 DataRow[] result = fkc.Table.NewRowArray(rows.Count);
870 rows.CopyTo(result, 0);
875 /// Gets the error description of the specified DataColumn.
877 public string GetColumnError (DataColumn column)
879 return GetColumnError (_table.Columns.IndexOf(column));
883 /// Gets the error description for the column specified by index.
885 public string GetColumnError (int columnIndex)
887 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
888 throw new IndexOutOfRangeException ();
890 string retVal = columnErrors[columnIndex];
892 retVal = string.Empty;
897 /// Gets the error description for the column, specified by name.
899 public string GetColumnError (string columnName)
901 return GetColumnError (_table.Columns.IndexOf(columnName));
905 /// Gets an array of columns that have errors.
907 public DataColumn[] GetColumnsInError ()
909 ArrayList dataColumns = new ArrayList ();
911 for (int i = 0; i < columnErrors.Length; i += 1)
913 if (columnErrors[i] != null && columnErrors[i] != String.Empty)
914 dataColumns.Add (_table.Columns[i]);
917 return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
921 /// Gets the parent row of a DataRow using the specified DataRelation.
923 public DataRow GetParentRow (DataRelation relation)
925 return GetParentRow (relation, DataRowVersion.Current);
929 /// Gets the parent row of a DataRow using the specified RelationName of a
932 public DataRow GetParentRow (string relationName)
934 return GetParentRow (relationName, DataRowVersion.Current);
938 /// Gets the parent row of a DataRow using the specified DataRelation, and
941 public DataRow GetParentRow (DataRelation relation, DataRowVersion version)
943 DataRow[] rows = GetParentRows(relation, version);
944 if (rows.Length == 0) return null;
949 /// Gets the parent row of a DataRow using the specified RelationName of a
950 /// DataRelation, and DataRowVersion.
952 public DataRow GetParentRow (string relationName, DataRowVersion version)
954 return GetParentRow (Table.DataSet.Relations[relationName], version);
958 /// Gets the parent rows of a DataRow using the specified DataRelation.
960 public DataRow[] GetParentRows (DataRelation relation)
962 return GetParentRows (relation, DataRowVersion.Current);
966 /// Gets the parent rows of a DataRow using the specified RelationName of a
969 public DataRow[] GetParentRows (string relationName)
971 return GetParentRows (relationName, DataRowVersion.Current);
975 /// Gets the parent rows of a DataRow using the specified DataRelation, and
978 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version)
980 // TODO: Caching for better preformance
981 if (relation == null)
982 return new DataRow[0];
984 if (this.Table == null || RowState == DataRowState.Detached)
985 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
987 if (relation.DataSet != this.Table.DataSet)
988 throw new ArgumentException();
990 ArrayList rows = new ArrayList();
991 DataColumn[] parentColumns = relation.ParentColumns;
992 DataColumn[] childColumns = relation.ChildColumns;
993 int numColumn = parentColumns.Length;
994 if (HasVersion(version))
996 object[] vals = new object[childColumns.Length];
997 for (int i = 0; i < vals.Length; i++)
998 vals[i] = this[childColumns[i], version];
1000 Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
1001 if (indx != null) { // get the child rows from the index
1002 Node[] childNodes = indx.FindAllSimple (vals);
1003 for (int i = 0; i < childNodes.Length; i++) {
1004 rows.Add (childNodes[i].Row);
1007 else { // no index so we have to search manualy.
1008 foreach (DataRow row in relation.ParentTable.Rows)
1010 bool allColumnsMatch = false;
1011 if (row.HasVersion(DataRowVersion.Default))
1013 allColumnsMatch = true;
1014 for (int columnCnt = 0; columnCnt < numColumn; columnCnt++)
1016 if (!this[childColumns[columnCnt], version].Equals(
1017 row[parentColumns[columnCnt], DataRowVersion.Default]))
1019 allColumnsMatch = false;
1024 if (allColumnsMatch) rows.Add(row);
1029 DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
1030 rows.CopyTo(result, 0);
1035 /// Gets the parent rows of a DataRow using the specified RelationName of a
1036 /// DataRelation, and DataRowVersion.
1038 public DataRow[] GetParentRows (string relationName, DataRowVersion version)
1040 return GetParentRows (Table.DataSet.Relations[relationName], version);
1044 /// Gets a value indicating whether a specified version exists.
1046 public bool HasVersion (DataRowVersion version)
1050 case DataRowVersion.Default:
1051 if (rowState == DataRowState.Deleted)
1053 if (rowState == DataRowState.Detached)
1054 return proposed != null;
1056 case DataRowVersion.Proposed:
1057 if (rowState == DataRowState.Deleted)
1059 return (proposed != null);
1060 case DataRowVersion.Current:
1061 if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
1063 return (current != null);
1064 case DataRowVersion.Original:
1065 if (rowState == DataRowState.Detached)
1067 return (original != null);
1073 /// Gets a value indicating whether the specified DataColumn contains a null value.
1075 public bool IsNull (DataColumn column)
1077 object o = this[column];
1078 return (o == DBNull.Value);
1082 /// Gets a value indicating whether the column at the specified index contains a null
1085 public bool IsNull (int columnIndex)
1087 object o = this[columnIndex];
1088 return (o == DBNull.Value);
1092 /// Gets a value indicating whether the named column contains a null value.
1094 public bool IsNull (string columnName)
1096 object o = this[columnName];
1097 return (o == DBNull.Value);
1101 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
1102 /// contains a null value.
1104 public bool IsNull (DataColumn column, DataRowVersion version)
1106 object o = this[column, version];
1107 return (o == DBNull.Value);
1111 /// Returns a value indicating whether all of the row columns specified contain a null value.
1113 internal bool IsNullColumns(DataColumn[] columns)
1115 bool allNull = true;
1116 for (int i = 0; i < columns.Length; i++)
1118 if (!IsNull(columns[i]))
1128 /// Rejects all changes made to the row since AcceptChanges was last called.
1130 public void RejectChanges ()
1132 if (RowState == DataRowState.Detached)
1133 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
1134 // If original is null, then nothing has happened since AcceptChanges
1135 // was last called. We have no "original" to go back to.
1136 if (original != null)
1138 Array.Copy (original, current, _table.Columns.Count);
1140 _table.ChangedDataRow (this, DataRowAction.Rollback);
1144 case DataRowState.Added:
1145 _table.DeleteRowFromIndexes (this);
1146 _table.Rows.RemoveInternal (this);
1148 case DataRowState.Modified:
1149 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1150 _table.Rows.ValidateDataRowInternal(this);
1151 rowState = DataRowState.Unchanged;
1153 case DataRowState.Deleted:
1154 rowState = DataRowState.Unchanged;
1155 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1156 _table.Rows.ValidateDataRowInternal(this);
1162 // If rows are just loaded via Xml the original values are null.
1163 // So in this case we have to remove all columns.
1164 // FIXME: I'm not realy sure, does this break something else, but
1167 if ((rowState & DataRowState.Added) > 0)
1169 _table.DeleteRowFromIndexes (this);
1170 _table.Rows.RemoveInternal (this);
1171 // if row was in Added state we move it to Detached.
1178 /// Sets the error description for a column specified as a DataColumn.
1180 public void SetColumnError (DataColumn column, string error)
1182 SetColumnError (_table.Columns.IndexOf (column), error);
1186 /// Sets the error description for a column specified by index.
1188 public void SetColumnError (int columnIndex, string error)
1190 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
1191 throw new IndexOutOfRangeException ();
1192 columnErrors[columnIndex] = error;
1196 /// Sets the error description for a column specified by name.
1198 public void SetColumnError (string columnName, string error)
1200 SetColumnError (_table.Columns.IndexOf (columnName), error);
1204 /// Sets the value of the specified DataColumn to a null value.
1206 protected void SetNull (DataColumn column)
1208 this[column] = DBNull.Value;
1212 /// Sets the parent row of a DataRow with specified new parent DataRow.
1214 public void SetParentRow (DataRow parentRow)
1216 SetParentRow(parentRow, null);
1220 /// Sets the parent row of a DataRow with specified new parent DataRow and
1223 public void SetParentRow (DataRow parentRow, DataRelation relation)
1225 if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
1226 throw new RowNotInTableException("This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.");
1228 if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
1229 throw new ArgumentException();
1232 if (relation == null)
1234 foreach (DataRelation parentRel in _table.ParentRelations)
1236 DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
1237 DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
1239 for (int i = 0; i < parentCols.Length; i++)
1241 if (parentRow == null)
1242 this[childCols[i].Ordinal] = DBNull.Value;
1244 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1251 DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
1252 DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
1254 for (int i = 0; i < parentCols.Length; i++)
1256 if (parentRow == null)
1257 this[childCols[i].Ordinal] = DBNull.Value;
1259 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1265 //Copy all values of this DataaRow to the row parameter.
1266 internal void CopyValuesToRow(DataRow row)
1270 throw new ArgumentNullException("row");
1272 throw new ArgumentException("'row' is the same as this object");
1274 DataColumnCollection columns = Table.Columns;
1276 for(int i = 0; i < columns.Count; i++){
1278 string columnName = columns[i].ColumnName;
1279 int index = row.Table.Columns.IndexOf(columnName);
1280 //if a column with the same name exists in both rows copy the values
1282 if (HasVersion(DataRowVersion.Original))
1284 if (row.original == null)
1285 row.original = new object[row.Table.Columns.Count];
1286 row.original[index] = row.SetColumnValue(original[i], index);
1288 if (HasVersion(DataRowVersion.Current))
1290 if (row.current == null)
1291 row.current = new object[row.Table.Columns.Count];
1292 row.current[index] = row.SetColumnValue(current[i], index);
1294 if (HasVersion(DataRowVersion.Proposed))
1296 if (row.proposed == null)
1297 row.proposed = new object[row.Table.Columns.Count];
1298 row.proposed[index] = row.SetColumnValue(proposed[i], index);
1301 //Saving the current value as the column value
1302 row[index] = row.current[index];
1307 row.rowState = RowState;
1308 row.RowError = RowError;
1309 row.columnErrors = columnErrors;
1313 public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
1315 // if a column is added we hava to add an additional value the
1316 // the priginal, current and propoed arrays.
1317 // this scenario can happened in merge operation.
1319 if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
1322 if (current != null)
1324 tmp = new object[current.Length + 1];
1325 Array.Copy (current, tmp, current.Length);
1326 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1329 if (proposed != null)
1331 tmp = new object[proposed.Length + 1];
1332 Array.Copy (proposed, tmp, proposed.Length);
1333 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1336 if(original != null)
1338 tmp = new object[original.Length + 1];
1339 Array.Copy (original, tmp, original.Length);
1340 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1347 internal bool IsRowChanged(DataRowState rowState) {
1348 if((RowState & rowState) != 0)
1351 //we need to find if child rows of this row changed.
1352 //if yes - we should return true
1354 // if the rowState is deleted we should get the original version of the row
1355 // else - we should get the current version of the row.
1356 DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
1357 int count = Table.ChildRelations.Count;
1358 for (int i = 0; i < count; i++){
1359 DataRelation rel = Table.ChildRelations[i];
1360 DataRow[] childRows = GetChildRows(rel, version);
1361 for (int j = 0; j < childRows.Length; j++){
1362 if (childRows[j].IsRowChanged(rowState))
1370 internal bool HasParentCollection
1374 return _hasParentCollection;
1378 _hasParentCollection = value;
1382 internal void CheckNullConstraints()
1384 if (_nullConstraintViolation)
1386 for (int i = 0; i < proposed.Length; i++)
1388 if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
1389 throw new NoNullAllowedException(_nullConstraintMessage);
1391 _nullConstraintViolation = false;
1395 #endregion // Methods