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 for (int c = 0; c < _table.Columns.Count; c++)
65 proposed[c] = DBNull.Value;
68 columnErrors = new string[_table.Columns.Count];
69 rowError = String.Empty;
71 //on first creating a DataRow it is always detached.
72 rowState = DataRowState.Detached;
74 foreach (DataColumn Col in _table.Columns) {
76 if (Col.AutoIncrement) {
77 this [Col] = Col.AutoIncrementValue();
80 _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
89 /// Gets a value indicating whether there are errors in a row.
91 public bool HasErrors {
94 if (RowError != string.Empty)
97 for (int i= 0; i < columnErrors.Length; i++){
98 if (columnErrors[i] != null && columnErrors[i] != string.Empty)
107 /// Gets or sets the data stored in the column specified by name.
109 public object this[string columnName] {
110 get { return this[columnName, DataRowVersion.Default]; }
112 int columnIndex = _table.Columns.IndexOf (columnName);
113 if (columnIndex == -1)
114 throw new IndexOutOfRangeException ();
115 this[columnIndex] = value;
120 /// Gets or sets the data stored in specified DataColumn
122 public object this[DataColumn column] {
125 return this[column, DataRowVersion.Default];}
127 int columnIndex = _table.Columns.IndexOf (column);
128 if (columnIndex == -1)
129 throw new ArgumentException ("The column does not belong to this table.");
130 this[columnIndex] = value;
135 /// Gets or sets the data stored in column specified by index.
137 public object this[int columnIndex] {
138 get { return this[columnIndex, DataRowVersion.Default]; }
140 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
141 throw new IndexOutOfRangeException ();
142 if (rowState == DataRowState.Deleted)
143 throw new DeletedRowInaccessibleException ();
144 DataColumn column = _table.Columns[columnIndex];
145 _table.ChangingDataColumn (this, column, value);
148 bool orginalEditing = editing;
149 if (!orginalEditing) BeginEdit ();
150 object v = SetColumnValue (value, columnIndex);
151 proposed[columnIndex] = v;
152 _table.ChangedDataColumn (this, column, v);
153 if (!orginalEditing) EndEdit ();
158 /// Gets the specified version of data stored in the named column.
160 public object this[string columnName, DataRowVersion version] {
162 int columnIndex = _table.Columns.IndexOf (columnName);
163 if (columnIndex == -1)
164 throw new IndexOutOfRangeException ();
165 return this[columnIndex, version];
170 /// Gets the specified version of data stored in the specified DataColumn.
172 public object this[DataColumn column, DataRowVersion version] {
174 if (column.Table != Table)
175 throw new ArgumentException ("The column does not belong to this table.");
176 int columnIndex = column.Ordinal;
177 return this[columnIndex, version];
182 /// Gets the data stored in the column, specified by index and version of the data to
185 public object this[int columnIndex, DataRowVersion version] {
187 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
188 throw new IndexOutOfRangeException ();
189 // Accessing deleted rows
190 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
191 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
193 //if (rowState == DataRowState.Detached && version == DataRowVersion.Default)
194 // 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.");
195 // Non-existent version
196 //if (!HasVersion (version))
197 // throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
198 if (HasVersion(version))
202 case DataRowVersion.Default:
203 if (editing || rowState == DataRowState.Detached)
204 return proposed[columnIndex];
205 return current[columnIndex];
206 case DataRowVersion.Proposed:
207 return proposed[columnIndex];
208 case DataRowVersion.Current:
209 return current[columnIndex];
210 case DataRowVersion.Original:
211 return original[columnIndex];
213 throw new ArgumentException ();
216 if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == null)
217 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.");
219 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
223 internal void SetOriginalValue (string columnName, object val)
225 int columnIndex = _table.Columns.IndexOf (columnName);
226 DataColumn column = _table.Columns[columnIndex];
227 _table.ChangingDataColumn (this, column, val);
229 if (original == null) original = new object [_table.Columns.Count];
230 val = SetColumnValue (val, columnIndex);
231 original[columnIndex] = val;
232 rowState = DataRowState.Modified;
236 /// Gets or sets all of the values for this row through an array.
239 public object[] ItemArray {
242 if (rowState == DataRowState.Detached)
243 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.");
244 // Accessing deleted rows
245 if (rowState == DataRowState.Deleted)
246 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
251 if (value.Length > _table.Columns.Count)
252 throw new ArgumentException ();
254 if (rowState == DataRowState.Deleted)
255 throw new DeletedRowInaccessibleException ();
257 object[] newItems = new object[_table.Columns.Count];
259 for (int i = 0; i < _table.Columns.Count; i++) {
261 if (i < value.Length)
266 newItems[i] = SetColumnValue (v, i);
269 bool orginalEditing = editing;
270 if (!orginalEditing) BeginEdit ();
272 if (!orginalEditing) EndEdit ();
276 private object SetColumnValue (object v, int index)
278 object newval = null;
279 DataColumn col = _table.Columns[index];
281 if (_hasParentCollection && col.ReadOnly && v != this[index])
282 throw new ReadOnlyException ();
286 if (col.DataType.ToString().Equals("System.Guid"))
287 throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
289 if(col.DefaultValue != DBNull.Value)
291 newval = col.DefaultValue;
293 else if(col.AutoIncrement == true)
295 newval = this [index];
299 if (!col.AllowDBNull)
301 //Constraint violations during data load is raise in DataTable EndLoad
302 this._nullConstraintViolation = true;
303 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
306 newval = DBNull.Value;
309 else if (v == DBNull.Value)
312 if (!col.AllowDBNull)
314 //Constraint violations during data load is raise in DataTable EndLoad
315 this._nullConstraintViolation = true;
316 _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
319 newval = DBNull.Value;
323 Type vType = v.GetType(); // data type of value
324 Type cType = col.DataType; // column data type
327 TypeCode typeCode = Type.GetTypeCode(cType);
329 case TypeCode.Boolean :
330 v = Convert.ToBoolean (v);
333 v = Convert.ToByte (v);
336 v = Convert.ToChar (v);
338 case TypeCode.DateTime :
339 v = Convert.ToDateTime (v);
341 case TypeCode.Decimal :
342 v = Convert.ToDecimal (v);
344 case TypeCode.Double :
345 v = Convert.ToDouble (v);
347 case TypeCode.Int16 :
348 v = Convert.ToInt16 (v);
350 case TypeCode.Int32 :
351 v = Convert.ToInt32 (v);
353 case TypeCode.Int64 :
354 v = Convert.ToInt64 (v);
356 case TypeCode.SByte :
357 v = Convert.ToSByte (v);
359 case TypeCode.Single :
360 v = Convert.ToSingle (v);
362 case TypeCode.String :
363 v = Convert.ToString (v);
365 case TypeCode.UInt16 :
366 v = Convert.ToUInt16 (v);
368 case TypeCode.UInt32 :
369 v = Convert.ToUInt32 (v);
371 case TypeCode.UInt64 :
372 v = Convert.ToUInt64 (v);
376 switch(cType.ToString()) {
377 case "System.TimeSpan" :
378 v = (System.TimeSpan) v;
383 case "System.Object" :
384 //v = (System.Object) v;
388 throw new InvalidCastException("Type not supported.");
396 // The MaxLength property is ignored for non-text columns
397 if ((Type.GetTypeCode(vType) == TypeCode.String) && (this.Table.Columns[index].MaxLength != -1) &&
398 (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
399 throw new ArgumentException("Cannot set column '" + this.Table.Columns[index].ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
402 if(col.AutoIncrement == true) {
403 long inc = Convert.ToInt64(v);
404 col.UpdateAutoIncrementValue (inc);
407 col.DataHasBeenSet = true;
412 /// Gets or sets the custom error description for a row.
414 public string RowError {
415 get { return rowError; }
416 set { rowError = value; }
420 /// Gets the current state of the row in regards to its relationship to the
421 /// DataRowCollection.
423 public DataRowState RowState {
424 get { return rowState; }
427 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
428 //to a Datatable so I added this method. Delete if there is a better way.
429 internal void AttachRow() {
432 rowState = DataRowState.Added;
435 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
436 //from a Datatable so I added this method. Delete if there is a better way.
437 internal void DetachRow() {
440 _hasParentCollection = false;
441 rowState = DataRowState.Detached;
445 /// Gets the DataTable for which this row has a schema.
447 public DataTable Table {
448 get { return _table; }
452 /// Gets and sets index of row. This is used from
455 internal int XmlRowID {
456 get { return xmlRowID; }
457 set { xmlRowID = value; }
461 /// Gets and sets index of row.
464 get { return _rowId; }
465 set { _rowId = value; }
473 /// Commits all the changes made to this row since the last time AcceptChanges was
476 public void AcceptChanges ()
478 EndEdit(); // in case it hasn't been called
480 case DataRowState.Added:
481 case DataRowState.Modified:
482 rowState = DataRowState.Unchanged;
484 case DataRowState.Deleted:
485 _table.Rows.RemoveInternal (this);
488 case DataRowState.Detached:
489 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
491 // Accept from detached
492 if (original == null)
493 original = new object[_table.Columns.Count];
494 Array.Copy (current, original, _table.Columns.Count);
498 /// Begins an edit operation on a DataRow object.
501 public void BeginEdit ()
503 if (rowState == DataRowState.Deleted)
504 throw new DeletedRowInaccessibleException ();
505 if (!HasVersion (DataRowVersion.Proposed)) {
506 proposed = new object[_table.Columns.Count];
507 Array.Copy (current, proposed, current.Length);
509 //TODO: Suspend validation
514 /// Cancels the current edit on the row.
517 public void CancelEdit ()
521 if (HasVersion (DataRowVersion.Proposed)) {
523 if (rowState == DataRowState.Modified)
524 rowState = DataRowState.Unchanged;
529 /// Clears the errors for the row, including the RowError and errors set with
532 public void ClearErrors ()
534 rowError = String.Empty;
535 columnErrors = new String[_table.Columns.Count];
539 /// Deletes the DataRow.
542 public void Delete ()
544 _table.DeletingDataRow(this, DataRowAction.Delete);
546 case DataRowState.Added:
547 // check what to do with child rows
548 CheckChildRows(DataRowAction.Delete);
549 _table.DeleteRowFromIndexes (this);
550 Table.Rows.RemoveInternal (this);
552 // if row was in Added state we move it to Detached.
555 case DataRowState.Deleted:
558 // check what to do with child rows
559 CheckChildRows(DataRowAction.Delete);
560 _table.DeleteRowFromIndexes (this);
561 rowState = DataRowState.Deleted;
564 _table.DeletedDataRow(this, DataRowAction.Delete);
567 // check the child rows of this row before deleting the row.
568 private void CheckChildRows(DataRowAction action)
571 // in this method we find the row that this row is in a relation with them.
572 // in shortly we find all child rows of this row.
573 // then we function according to the DeleteRule of the foriegnkey.
576 // 1. find if this row is attached to dataset.
577 // 2. find if EnforceConstraints is true.
578 // 3. find if there are any constraint on the table that the row is in.
579 if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
581 foreach (DataTable table in _table.DataSet.Tables)
583 // loop on all ForeignKeyConstrain of the table.
584 foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
586 if (fk.RelatedTable == _table)
589 if (action == DataRowAction.Delete)
590 rule = fk.DeleteRule;
592 rule = fk.UpdateRule;
593 CheckChildRows(fk, action, rule);
600 private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
602 DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
605 case Rule.Cascade: // delete or change all relted rows.
606 if (childRows != null)
608 for (int j = 0; j < childRows.Length; j++)
610 // if action is delete we delete all child rows
611 if (action == DataRowAction.Delete)
613 if (childRows[j].RowState != DataRowState.Deleted)
614 childRows[j].Delete();
616 // if action is change we change the values in the child row
617 else if (action == DataRowAction.Change)
619 // change only the values in the key columns
620 // set the childcolumn value to the new parent row value
621 for (int k = 0; k < fkc.Columns.Length; k++)
622 childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
627 case Rule.None: // throw an exception if there are any child rows.
628 if (childRows != null)
630 for (int j = 0; j < childRows.Length; j++)
632 if (childRows[j].RowState != DataRowState.Deleted)
634 string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
635 string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
636 string message = action == DataRowAction.Delete ? delStr : changeStr;
637 throw new InvalidConstraintException(message);
642 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
643 if (childRows != null)
645 for (int j = 0; j < childRows.Length; j++)
647 DataRow child = childRows[j];
648 if (childRows[j].RowState != DataRowState.Deleted)
650 //set only the key columns to default
651 for (int k = 0; k < fkc.Columns.Length; k++)
652 child[fkc.Columns[k]] = fkc.Columns[k].DefaultValue;
657 case Rule.SetNull: // set the values in the child row to null.
658 if (childRows != null)
660 for (int j = 0; j < childRows.Length; j++)
662 DataRow child = childRows[j];
663 if (childRows[j].RowState != DataRowState.Deleted)
665 // set only the key columns to DBNull
666 for (int k = 0; k < fkc.Columns.Length; k++)
667 child.SetNull(fkc.Columns[k]);
677 /// Ends the edit occurring on the row.
680 public void EndEdit ()
682 if (_inChangingEvent)
683 throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
684 if (rowState == DataRowState.Detached)
690 if (HasVersion (DataRowVersion.Proposed))
692 _inChangingEvent = true;
695 _table.ChangingDataRow(this, DataRowAction.Change);
699 _inChangingEvent = false;
701 if (rowState == DataRowState.Unchanged)
702 rowState = DataRowState.Modified;
704 //Calling next method validates UniqueConstraints
708 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
709 _table.Rows.ValidateDataRowInternal(this);
718 // Now we are going to check all child rows of current row.
719 // In the case the cascade is true the child rows will look up for
720 // parent row. since lookup in index is always on current,
721 // we have to move proposed version of current row to current
722 // in the case of check child row failure we are rolling
723 // current row state back.
724 object[] backup = current;
727 bool editing_backup = editing;
730 // check all child rows.
731 CheckChildRows(DataRowAction.Change);
733 catch (Exception ex) {
734 // if check child rows failed - rollback to previous state
735 // i.e. restore proposed and current versions
738 editing = editing_backup;
739 // since we failed - propagate an exception
742 _table.ChangedDataRow(this, DataRowAction.Change);
747 /// Gets the child rows of this DataRow using the specified DataRelation.
749 public DataRow[] GetChildRows (DataRelation relation)
751 return GetChildRows (relation, DataRowVersion.Current);
755 /// Gets the child rows of a DataRow using the specified RelationName of a
758 public DataRow[] GetChildRows (string relationName)
760 return GetChildRows (Table.DataSet.Relations[relationName]);
764 /// Gets the child rows of a DataRow using the specified DataRelation, and
767 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version)
769 if (relation == null)
770 return new DataRow[0];
772 if (this.Table == null || RowState == DataRowState.Detached)
773 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.");
775 if (relation.DataSet != this.Table.DataSet)
776 throw new ArgumentException();
778 if (relation.ChildKeyConstraint != null)
779 return GetChildRows (relation.ChildKeyConstraint, version);
781 ArrayList rows = new ArrayList();
782 DataColumn[] parentColumns = relation.ParentColumns;
783 DataColumn[] childColumns = relation.ChildColumns;
784 int numColumn = parentColumns.Length;
785 if (HasVersion(version))
787 object[] vals = new object[parentColumns.Length];
788 for (int i = 0; i < vals.Length; i++)
789 vals[i] = this[parentColumns[i], version];
791 foreach (DataRow row in relation.ChildTable.Rows)
793 bool allColumnsMatch = false;
794 if (row.HasVersion(DataRowVersion.Default))
796 allColumnsMatch = true;
797 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
799 if (!vals[columnCnt].Equals(
800 row[childColumns[columnCnt], DataRowVersion.Default]))
802 allColumnsMatch = false;
807 if (allColumnsMatch) rows.Add(row);
810 DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
811 rows.CopyTo(result, 0);
816 /// Gets the child rows of a DataRow using the specified RelationName of a
817 /// DataRelation, and DataRowVersion.
819 public DataRow[] GetChildRows (string relationName, DataRowVersion version)
821 return GetChildRows (Table.DataSet.Relations[relationName], version);
824 private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version)
826 ArrayList rows = new ArrayList();
827 DataColumn[] parentColumns = fkc.RelatedColumns;
828 DataColumn[] childColumns = fkc.Columns;
829 int numColumn = parentColumns.Length;
830 if (HasVersion(version))
832 object[] vals = new object[parentColumns.Length];
833 for (int i = 0; i < vals.Length; i++)
834 vals[i] = this[parentColumns[i], version];
836 Index index = fkc.Index;
838 // get the child rows from the index
839 Node[] childNodes = index.FindAllSimple (vals);
840 for (int i = 0; i < childNodes.Length; i++) {
841 rows.Add (childNodes[i].Row);
844 else { // if there is no index we search manualy.
845 foreach (DataRow row in fkc.Table.Rows)
847 bool allColumnsMatch = false;
848 if (row.HasVersion(DataRowVersion.Default))
850 allColumnsMatch = true;
851 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
853 if (!vals[columnCnt].Equals(
854 row[childColumns[columnCnt], DataRowVersion.Default]))
856 allColumnsMatch = false;
861 if (allColumnsMatch) rows.Add(row);
866 DataRow[] result = fkc.Table.NewRowArray(rows.Count);
867 rows.CopyTo(result, 0);
872 /// Gets the error description of the specified DataColumn.
874 public string GetColumnError (DataColumn column)
876 return GetColumnError (_table.Columns.IndexOf(column));
880 /// Gets the error description for the column specified by index.
882 public string GetColumnError (int columnIndex)
884 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
885 throw new IndexOutOfRangeException ();
887 string retVal = columnErrors[columnIndex];
889 retVal = string.Empty;
894 /// Gets the error description for the column, specified by name.
896 public string GetColumnError (string columnName)
898 return GetColumnError (_table.Columns.IndexOf(columnName));
902 /// Gets an array of columns that have errors.
904 public DataColumn[] GetColumnsInError ()
906 ArrayList dataColumns = new ArrayList ();
908 for (int i = 0; i < columnErrors.Length; i += 1)
910 if (columnErrors[i] != null && columnErrors[i] != String.Empty)
911 dataColumns.Add (_table.Columns[i]);
914 return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
918 /// Gets the parent row of a DataRow using the specified DataRelation.
920 public DataRow GetParentRow (DataRelation relation)
922 return GetParentRow (relation, DataRowVersion.Current);
926 /// Gets the parent row of a DataRow using the specified RelationName of a
929 public DataRow GetParentRow (string relationName)
931 return GetParentRow (relationName, DataRowVersion.Current);
935 /// Gets the parent row of a DataRow using the specified DataRelation, and
938 public DataRow GetParentRow (DataRelation relation, DataRowVersion version)
940 DataRow[] rows = GetParentRows(relation, version);
941 if (rows.Length == 0) return null;
946 /// Gets the parent row of a DataRow using the specified RelationName of a
947 /// DataRelation, and DataRowVersion.
949 public DataRow GetParentRow (string relationName, DataRowVersion version)
951 return GetParentRow (Table.DataSet.Relations[relationName], version);
955 /// Gets the parent rows of a DataRow using the specified DataRelation.
957 public DataRow[] GetParentRows (DataRelation relation)
959 return GetParentRows (relation, DataRowVersion.Current);
963 /// Gets the parent rows of a DataRow using the specified RelationName of a
966 public DataRow[] GetParentRows (string relationName)
968 return GetParentRows (relationName, DataRowVersion.Current);
972 /// Gets the parent rows of a DataRow using the specified DataRelation, and
975 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version)
977 // TODO: Caching for better preformance
978 if (relation == null)
979 return new DataRow[0];
981 if (this.Table == null || RowState == DataRowState.Detached)
982 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.");
984 if (relation.DataSet != this.Table.DataSet)
985 throw new ArgumentException();
987 ArrayList rows = new ArrayList();
988 DataColumn[] parentColumns = relation.ParentColumns;
989 DataColumn[] childColumns = relation.ChildColumns;
990 int numColumn = parentColumns.Length;
991 if (HasVersion(version))
993 object[] vals = new object[childColumns.Length];
994 for (int i = 0; i < vals.Length; i++)
995 vals[i] = this[childColumns[i], version];
997 Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
998 if (indx != null) { // get the child rows from the index
999 Node[] childNodes = indx.FindAllSimple (vals);
1000 for (int i = 0; i < childNodes.Length; i++) {
1001 rows.Add (childNodes[i].Row);
1004 else { // no index so we have to search manualy.
1005 foreach (DataRow row in relation.ParentTable.Rows)
1007 bool allColumnsMatch = false;
1008 if (row.HasVersion(DataRowVersion.Default))
1010 allColumnsMatch = true;
1011 for (int columnCnt = 0; columnCnt < numColumn; columnCnt++)
1013 if (!this[childColumns[columnCnt], version].Equals(
1014 row[parentColumns[columnCnt], DataRowVersion.Default]))
1016 allColumnsMatch = false;
1021 if (allColumnsMatch) rows.Add(row);
1026 DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
1027 rows.CopyTo(result, 0);
1032 /// Gets the parent rows of a DataRow using the specified RelationName of a
1033 /// DataRelation, and DataRowVersion.
1035 public DataRow[] GetParentRows (string relationName, DataRowVersion version)
1037 return GetParentRows (Table.DataSet.Relations[relationName], version);
1041 /// Gets a value indicating whether a specified version exists.
1043 public bool HasVersion (DataRowVersion version)
1047 case DataRowVersion.Default:
1048 if (rowState == DataRowState.Deleted)
1050 if (rowState == DataRowState.Detached)
1051 return proposed != null;
1053 case DataRowVersion.Proposed:
1054 if (rowState == DataRowState.Deleted)
1056 return (proposed != null);
1057 case DataRowVersion.Current:
1058 if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
1060 return (current != null);
1061 case DataRowVersion.Original:
1062 if (rowState == DataRowState.Detached)
1064 return (original != null);
1070 /// Gets a value indicating whether the specified DataColumn contains a null value.
1072 public bool IsNull (DataColumn column)
1074 object o = this[column];
1075 return (o == DBNull.Value);
1079 /// Gets a value indicating whether the column at the specified index contains a null
1082 public bool IsNull (int columnIndex)
1084 object o = this[columnIndex];
1085 return (o == DBNull.Value);
1089 /// Gets a value indicating whether the named column contains a null value.
1091 public bool IsNull (string columnName)
1093 object o = this[columnName];
1094 return (o == DBNull.Value);
1098 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
1099 /// contains a null value.
1101 public bool IsNull (DataColumn column, DataRowVersion version)
1103 object o = this[column, version];
1104 return (o == DBNull.Value);
1108 /// Returns a value indicating whether all of the row columns specified contain a null value.
1110 internal bool IsNullColumns(DataColumn[] columns)
1112 bool allNull = true;
1113 for (int i = 0; i < columns.Length; i++)
1115 if (!IsNull(columns[i]))
1125 /// Rejects all changes made to the row since AcceptChanges was last called.
1127 public void RejectChanges ()
1129 if (RowState == DataRowState.Detached)
1130 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.");
1131 // If original is null, then nothing has happened since AcceptChanges
1132 // was last called. We have no "original" to go back to.
1133 if (original != null)
1135 Array.Copy (original, current, _table.Columns.Count);
1137 _table.ChangedDataRow (this, DataRowAction.Rollback);
1141 case DataRowState.Added:
1142 _table.DeleteRowFromIndexes (this);
1143 _table.Rows.RemoveInternal (this);
1145 case DataRowState.Modified:
1146 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1147 _table.Rows.ValidateDataRowInternal(this);
1148 rowState = DataRowState.Unchanged;
1150 case DataRowState.Deleted:
1151 rowState = DataRowState.Unchanged;
1152 if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
1153 _table.Rows.ValidateDataRowInternal(this);
1159 // If rows are just loaded via Xml the original values are null.
1160 // So in this case we have to remove all columns.
1161 // FIXME: I'm not realy sure, does this break something else, but
1164 if ((rowState & DataRowState.Added) > 0)
1166 _table.DeleteRowFromIndexes (this);
1167 _table.Rows.RemoveInternal (this);
1168 // if row was in Added state we move it to Detached.
1175 /// Sets the error description for a column specified as a DataColumn.
1177 public void SetColumnError (DataColumn column, string error)
1179 SetColumnError (_table.Columns.IndexOf (column), error);
1183 /// Sets the error description for a column specified by index.
1185 public void SetColumnError (int columnIndex, string error)
1187 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
1188 throw new IndexOutOfRangeException ();
1189 columnErrors[columnIndex] = error;
1193 /// Sets the error description for a column specified by name.
1195 public void SetColumnError (string columnName, string error)
1197 SetColumnError (_table.Columns.IndexOf (columnName), error);
1201 /// Sets the value of the specified DataColumn to a null value.
1203 protected void SetNull (DataColumn column)
1205 this[column] = DBNull.Value;
1209 /// Sets the parent row of a DataRow with specified new parent DataRow.
1212 public void SetParentRow (DataRow parentRow)
1214 SetParentRow(parentRow, null);
1218 /// Sets the parent row of a DataRow with specified new parent DataRow and
1222 public void SetParentRow (DataRow parentRow, DataRelation relation)
1224 if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
1225 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.");
1227 if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
1228 throw new ArgumentException();
1231 if (relation == null)
1233 foreach (DataRelation parentRel in _table.ParentRelations)
1235 DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
1236 DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
1238 for (int i = 0; i < parentCols.Length; i++)
1240 if (parentRow == null)
1241 this[childCols[i].Ordinal] = DBNull.Value;
1243 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1250 DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
1251 DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
1253 for (int i = 0; i < parentCols.Length; i++)
1255 if (parentRow == null)
1256 this[childCols[i].Ordinal] = DBNull.Value;
1258 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
1264 //Copy all values of this DataaRow to the row parameter.
1265 internal void CopyValuesToRow(DataRow row)
1269 throw new ArgumentNullException("row");
1271 throw new ArgumentException("'row' is the same as this object");
1273 DataColumnCollection columns = Table.Columns;
1275 for(int i = 0; i < columns.Count; i++){
1277 string columnName = columns[i].ColumnName;
1278 int index = row.Table.Columns.IndexOf(columnName);
1279 //if a column with the same name exists in both rows copy the values
1281 if (HasVersion(DataRowVersion.Original))
1283 if (row.original == null)
1284 row.original = new object[row.Table.Columns.Count];
1285 row.original[index] = row.SetColumnValue(original[i], index);
1287 if (HasVersion(DataRowVersion.Current))
1289 if (row.current == null)
1290 row.current = new object[row.Table.Columns.Count];
1291 row.current[index] = row.SetColumnValue(current[i], index);
1293 if (HasVersion(DataRowVersion.Proposed))
1295 if (row.proposed == null)
1296 row.proposed = new object[row.Table.Columns.Count];
1297 row.proposed[index] = row.SetColumnValue(proposed[i], index);
1300 //Saving the current value as the column value
1301 row[index] = row.current[index];
1306 row.rowState = RowState;
1307 row.RowError = RowError;
1308 row.columnErrors = columnErrors;
1312 public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
1314 // if a column is added we hava to add an additional value the
1315 // the priginal, current and propoed arrays.
1316 // this scenario can happened in merge operation.
1318 if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
1321 if (current != null)
1323 tmp = new object[current.Length + 1];
1324 Array.Copy (current, tmp, current.Length);
1325 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1328 if (proposed != null)
1330 tmp = new object[proposed.Length + 1];
1331 Array.Copy (proposed, tmp, proposed.Length);
1332 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1335 if(original != null)
1337 tmp = new object[original.Length + 1];
1338 Array.Copy (original, tmp, original.Length);
1339 tmp[tmp.Length - 1] = SetColumnValue(null, tmp.Length - 1);
1346 internal bool IsRowChanged(DataRowState rowState) {
1347 if((RowState & rowState) != 0)
1350 //we need to find if child rows of this row changed.
1351 //if yes - we should return true
1353 // if the rowState is deleted we should get the original version of the row
1354 // else - we should get the current version of the row.
1355 DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
1356 int count = Table.ChildRelations.Count;
1357 for (int i = 0; i < count; i++){
1358 DataRelation rel = Table.ChildRelations[i];
1359 DataRow[] childRows = GetChildRows(rel, version);
1360 for (int j = 0; j < childRows.Length; j++){
1361 if (childRows[j].IsRowChanged(rowState))
1369 internal bool HasParentCollection
1373 return _hasParentCollection;
1377 _hasParentCollection = value;
1381 internal void CheckNullConstraints()
1383 if (_nullConstraintViolation)
1385 for (int i = 0; i < proposed.Length; i++)
1387 if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
1388 throw new NoNullAllowedException(_nullConstraintMessage);
1390 _nullConstraintViolation = false;
1394 #endregion // Methods