2 // System.Data.DataRow.cs
\r
5 // Rodrigo Moya <rodrigo@ximian.com>
\r
6 // Daniel Morgan <danmorg@sc.rr.com>
\r
7 // Tim Coleman <tim@timcoleman.com>
\r
8 // Ville Palo <vi64pa@koti.soon.fi>
\r
9 // Alan Tam Siu Lung <Tam@SiuLung.com>
\r
11 // (C) Ximian, Inc 2002
\r
12 // (C) Daniel Morgan 2002, 2003
\r
13 // Copyright (C) 2002 Tim Coleman
\r
17 using System.Collections;
\r
18 using System.Globalization;
\r
20 namespace System.Data {
\r
22 /// Represents a row of data in a DataTable.
\r
25 public class DataRow
\r
29 private DataTable _table;
\r
31 private object[] original;
\r
32 private object[] proposed;
\r
33 private object[] current;
\r
35 private string[] columnErrors;
\r
36 private string rowError;
\r
37 private DataRowState rowState;
\r
38 internal int xmlRowID = 0;
\r
39 internal bool _nullConstraintViolation;
\r
40 private bool editing = false;
\r
41 private bool _hasParentCollection;
\r
45 #region Constructors
\r
48 /// This member supports the .NET Framework infrastructure and is not intended to be
\r
49 /// used directly from your code.
\r
51 protected internal DataRow (DataRowBuilder builder)
\r
53 _table = builder.Table;
\r
57 proposed = new object[_table.Columns.Count];
\r
58 for (int c = 0; c < _table.Columns.Count; c++)
\r
60 proposed[c] = DBNull.Value;
\r
63 columnErrors = new string[_table.Columns.Count];
\r
64 rowError = String.Empty;
\r
66 //on first creating a DataRow it is always detached.
\r
67 rowState = DataRowState.Detached;
\r
69 foreach (DataColumn Col in _table.Columns) {
\r
71 if (Col.AutoIncrement) {
\r
72 this [Col] = Col.AutoIncrementValue();
\r
75 _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
\r
84 /// Gets a value indicating whether there are errors in a row.
\r
86 public bool HasErrors {
\r
89 if (RowError != string.Empty)
\r
92 for (int i= 0; i < columnErrors.Length; i++){
\r
93 if (columnErrors[i] != null && columnErrors[i] != string.Empty)
\r
102 /// Gets or sets the data stored in the column specified by name.
\r
104 public object this[string columnName] {
\r
105 get { return this[columnName, DataRowVersion.Default]; }
\r
107 int columnIndex = _table.Columns.IndexOf (columnName);
\r
108 if (columnIndex == -1)
\r
109 throw new IndexOutOfRangeException ();
\r
110 this[columnIndex] = value;
\r
115 /// Gets or sets the data stored in specified DataColumn
\r
117 public object this[DataColumn column] {
\r
120 return this[column, DataRowVersion.Default];}
\r
122 int columnIndex = _table.Columns.IndexOf (column);
\r
123 if (columnIndex == -1)
\r
124 throw new ArgumentException ("The column does not belong to this table.");
\r
125 this[columnIndex] = value;
\r
130 /// Gets or sets the data stored in column specified by index.
\r
132 public object this[int columnIndex] {
\r
133 get { return this[columnIndex, DataRowVersion.Default]; }
\r
135 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
\r
136 throw new IndexOutOfRangeException ();
\r
137 if (rowState == DataRowState.Deleted)
\r
138 throw new DeletedRowInaccessibleException ();
\r
139 DataColumn column = _table.Columns[columnIndex];
\r
140 _table.ChangingDataColumn (this, column, value);
\r
143 bool orginalEditing = editing;
\r
144 if (!orginalEditing) BeginEdit ();
\r
145 object v = SetColumnValue (value, columnIndex);
\r
146 proposed[columnIndex] = v;
\r
147 _table.ChangedDataColumn (this, column, v);
\r
148 if (!orginalEditing) EndEdit ();
\r
153 /// Gets the specified version of data stored in the named column.
\r
155 public object this[string columnName, DataRowVersion version] {
\r
157 int columnIndex = _table.Columns.IndexOf (columnName);
\r
158 if (columnIndex == -1)
\r
159 throw new IndexOutOfRangeException ();
\r
160 return this[columnIndex, version];
\r
165 /// Gets the specified version of data stored in the specified DataColumn.
\r
167 public object this[DataColumn column, DataRowVersion version] {
\r
169 int columnIndex = _table.Columns.IndexOf (column);
\r
170 if (columnIndex == -1)
\r
171 throw new ArgumentException ("The column does not belong to this table.");
\r
172 return this[columnIndex, version];
\r
177 /// Gets the data stored in the column, specified by index and version of the data to
\r
180 public object this[int columnIndex, DataRowVersion version] {
\r
182 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
\r
183 throw new IndexOutOfRangeException ();
\r
184 // Non-existent version
\r
185 if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
\r
186 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
\r
187 // Accessing deleted rows
\r
188 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
\r
189 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
\r
191 case DataRowVersion.Default:
\r
192 if (editing || rowState == DataRowState.Detached)
\r
193 return proposed[columnIndex];
\r
194 return current[columnIndex];
\r
195 case DataRowVersion.Proposed:
\r
196 return proposed[columnIndex];
\r
197 case DataRowVersion.Current:
\r
198 return current[columnIndex];
\r
199 case DataRowVersion.Original:
\r
200 return original[columnIndex];
\r
202 throw new ArgumentException ();
\r
208 /// Gets or sets all of the values for this row through an array.
\r
211 public object[] ItemArray {
\r
216 if (value.Length > _table.Columns.Count)
\r
217 throw new ArgumentException ();
\r
219 if (rowState == DataRowState.Deleted)
\r
220 throw new DeletedRowInaccessibleException ();
\r
222 object[] newItems = new object[_table.Columns.Count];
\r
224 for (int i = 0; i < _table.Columns.Count; i++) {
\r
226 if (i < value.Length)
\r
231 newItems[i] = SetColumnValue (v, i);
\r
234 bool orginalEditing = editing;
\r
235 if (!orginalEditing) BeginEdit ();
\r
236 proposed = newItems;
\r
237 if (!orginalEditing) EndEdit ();
\r
241 private object SetColumnValue (object v, int index)
\r
243 object newval = null;
\r
244 DataColumn col = _table.Columns[index];
\r
246 if (_hasParentCollection && col.ReadOnly && v != this[index])
\r
247 throw new ReadOnlyException ();
\r
251 if(col.DefaultValue != DBNull.Value)
\r
253 newval = col.DefaultValue;
\r
255 else if(col.AutoIncrement == true)
\r
257 newval = this [index];
\r
261 if (!col.AllowDBNull)
\r
263 if (!this._table._duringDataLoad)
\r
265 throw new NoNullAllowedException ();
\r
269 //Constraint violations during data load is raise in DataTable EndLoad
\r
270 this._nullConstraintViolation = true;
\r
275 newval= DBNull.Value;
\r
280 else if (v == DBNull.Value)
\r
283 if (!col.AllowDBNull)
\r
285 if (!this._table._duringDataLoad)
\r
287 throw new NoNullAllowedException ();
\r
291 //Constraint violations during data load is raise in DataTable EndLoad
\r
292 this._nullConstraintViolation = true;
\r
296 newval= DBNull.Value;
\r
300 Type vType = v.GetType(); // data type of value
\r
301 Type cType = col.DataType; // column data type
\r
302 if (cType != vType)
\r
304 TypeCode typeCode = Type.GetTypeCode(cType);
\r
306 case TypeCode.Boolean :
\r
307 v = Convert.ToBoolean (v);
\r
309 case TypeCode.Byte :
\r
310 v = Convert.ToByte (v);
\r
312 case TypeCode.Char :
\r
313 v = Convert.ToChar (v);
\r
315 case TypeCode.DateTime :
\r
316 v = Convert.ToDateTime (v);
\r
318 case TypeCode.Decimal :
\r
319 v = Convert.ToDecimal (v);
\r
321 case TypeCode.Double :
\r
322 v = Convert.ToDouble (v);
\r
324 case TypeCode.Int16 :
\r
325 v = Convert.ToInt16 (v);
\r
327 case TypeCode.Int32 :
\r
328 v = Convert.ToInt32 (v);
\r
330 case TypeCode.Int64 :
\r
331 v = Convert.ToInt64 (v);
\r
333 case TypeCode.SByte :
\r
334 v = Convert.ToSByte (v);
\r
336 case TypeCode.Single :
\r
337 v = Convert.ToSingle (v);
\r
339 case TypeCode.String :
\r
340 v = Convert.ToString (v);
\r
342 case TypeCode.UInt16 :
\r
343 v = Convert.ToUInt16 (v);
\r
345 case TypeCode.UInt32 :
\r
346 v = Convert.ToUInt32 (v);
\r
348 case TypeCode.UInt64 :
\r
349 v = Convert.ToUInt64 (v);
\r
352 switch(cType.ToString()) {
\r
353 case "System.TimeSpan" :
\r
354 v = (System.TimeSpan) v;
\r
356 case "System.Type" :
\r
357 v = (System.Type) v;
\r
359 case "System.Object" :
\r
360 //v = (System.Object) v;
\r
363 // FIXME: is exception correct?
\r
364 throw new InvalidCastException("Type not supported.");
\r
368 vType = v.GetType();
\r
371 if(col.AutoIncrement == true) {
\r
372 long inc = Convert.ToInt64(v);
\r
373 col.UpdateAutoIncrementValue (inc);
\r
376 col.DataHasBeenSet = true;
\r
381 /// Gets or sets the custom error description for a row.
\r
383 public string RowError {
\r
384 get { return rowError; }
\r
385 set { rowError = value; }
\r
389 /// Gets the current state of the row in regards to its relationship to the
\r
390 /// DataRowCollection.
\r
392 public DataRowState RowState {
\r
393 get { return rowState; }
\r
396 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
\r
397 //to a Datatable so I added this method. Delete if there is a better way.
\r
398 internal void AttachRow() {
\r
399 current = proposed;
\r
401 rowState = DataRowState.Added;
\r
404 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
\r
405 //from a Datatable so I added this method. Delete if there is a better way.
\r
406 internal void DetachRow() {
\r
408 _hasParentCollection = false;
\r
409 rowState = DataRowState.Detached;
\r
413 /// Gets the DataTable for which this row has a schema.
\r
415 public DataTable Table {
\r
416 get { return _table; }
\r
420 /// Gets and sets index of row. This is used from
\r
421 /// XmlDataDocument.
\r
423 internal int XmlRowID {
\r
424 get { return xmlRowID; }
\r
425 set { xmlRowID = value; }
\r
433 /// Commits all the changes made to this row since the last time AcceptChanges was
\r
436 public void AcceptChanges ()
\r
438 EndEdit(); // in case it hasn't been called
\r
439 switch (rowState) {
\r
440 case DataRowState.Added:
\r
441 case DataRowState.Modified:
\r
442 rowState = DataRowState.Unchanged;
\r
444 case DataRowState.Deleted:
\r
445 _table.Rows.Remove (this);
\r
447 case DataRowState.Detached:
\r
448 throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
\r
450 // Accept from detached
\r
451 if (original == null)
\r
452 original = new object[_table.Columns.Count];
\r
453 Array.Copy (current, original, _table.Columns.Count);
\r
457 /// Begins an edit operation on a DataRow object.
\r
460 public void BeginEdit ()
\r
462 if (rowState == DataRowState.Deleted)
\r
463 throw new DeletedRowInaccessibleException ();
\r
464 if (!HasVersion (DataRowVersion.Proposed)) {
\r
465 proposed = new object[_table.Columns.Count];
\r
466 Array.Copy (current, proposed, current.Length);
\r
468 //TODO: Suspend validation
\r
473 /// Cancels the current edit on the row.
\r
476 public void CancelEdit ()
\r
480 if (HasVersion (DataRowVersion.Proposed)) {
\r
482 if (rowState == DataRowState.Modified)
\r
483 rowState = DataRowState.Unchanged;
\r
488 /// Clears the errors for the row, including the RowError and errors set with
\r
489 /// SetColumnError.
\r
491 public void ClearErrors ()
\r
493 rowError = String.Empty;
\r
494 columnErrors = new String[_table.Columns.Count];
\r
498 /// Deletes the DataRow.
\r
501 public void Delete ()
\r
503 switch (rowState) {
\r
504 case DataRowState.Added:
\r
505 Table.Rows.Remove (this);
\r
507 case DataRowState.Deleted:
\r
508 throw new DeletedRowInaccessibleException ();
\r
510 _table.DeletingDataRow(this, DataRowAction.Delete);
\r
511 // check what to do with child rows
\r
512 CheckChildRows(DataRowAction.Delete);
\r
513 rowState = DataRowState.Deleted;
\r
514 _table.DeletedDataRow(this, DataRowAction.Delete);
\r
519 // check the child rows of this row before deleting the row.
\r
520 private void CheckChildRows(DataRowAction action)
\r
523 // in this method we find the row that this row is in a reltion with them.
\r
524 // in shortly we find all child rows of this row.
\r
525 // then we function according to the DeleteRule of the foriegnkey.
\r
528 // 1. find if this row is attached to dataset.
\r
529 // 2. find if EnforceConstraints is true.
\r
530 // 3. find if there are any constraint on the table that the row is in.
\r
531 if (_table.DataSet != null && _table.DataSet.EnforceConstraints && _table.Constraints.Count > 0)
\r
533 foreach (DataTable table in _table.DataSet.Tables)
\r
535 // loop on all constraints of the table.
\r
536 ConstraintCollection constraintsCollection = table.Constraints;
\r
537 for (int i = 0; i < constraintsCollection.Count; i++)
\r
539 ForeignKeyConstraint fk = null;
\r
540 if (constraintsCollection[i] is ForeignKeyConstraint)
\r
542 fk = (ForeignKeyConstraint)constraintsCollection[i];
\r
543 if (fk.RelatedTable == _table)
\r
545 //we create a dummy relation because we do not want to duplicate code of GetChild().
\r
546 // we use the dummy relation to find child rows.
\r
547 DataRelation rel = new DataRelation("dummy", fk.RelatedColumns, fk.Columns, false);
\r
549 if (action == DataRowAction.Delete)
\r
550 rule = fk.DeleteRule;
\r
552 rule = fk.UpdateRule;
\r
553 CheckChildRows(rel, action, rule);
\r
561 private void CheckChildRows(DataRelation rel, DataRowAction action, Rule rule)
\r
563 DataRow[] childRows = GetChildRows(rel);
\r
566 case Rule.Cascade: // delete or change all relted rows.
\r
567 if (childRows != null)
\r
569 for (int j = 0; j < childRows.Length; j++)
\r
571 // if action is delete we delete all child rows
\r
572 if (action == DataRowAction.Delete)
\r
574 if (childRows[j].RowState != DataRowState.Deleted)
\r
575 childRows[j].Delete();
\r
577 // if action is change we change the values in the child row
\r
578 else if (action == DataRowAction.Change)
\r
580 // change only the values in the key columns
\r
581 // set the childcolumn value to the new parent row value
\r
582 for (int k = 0; k < rel.ChildColumns.Length; k++)
\r
583 childRows[j][rel.ChildColumns[k]] = this[rel.ParentColumns[k], DataRowVersion.Proposed];
\r
588 case Rule.None: // throw an exception if there are any child rows.
\r
589 if (childRows != null)
\r
591 for (int j = 0; j < childRows.Length; j++)
\r
593 if (childRows[j].RowState != DataRowState.Deleted)
\r
595 string changeStr = "Cannot change this row because constraints are enforced on relation " + rel.RelationName +", and changing this row will strand child rows.";
\r
596 string delStr = "Cannot delete this row because constraints are enforced on relation " + rel.RelationName +", and deleting this row will strand child rows.";
\r
597 string message = action == DataRowAction.Delete ? delStr : changeStr;
\r
598 throw new InvalidConstraintException(message);
\r
603 case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
\r
604 if (childRows != null)
\r
606 for (int j = 0; j < childRows.Length; j++)
\r
608 DataRow child = childRows[j];
\r
609 if (childRows[j].RowState != DataRowState.Deleted)
\r
611 //set only the key columns to default
\r
612 for (int k = 0; k < rel.ChildColumns.Length; k++)
\r
613 child[rel.ChildColumns[k]] = rel.ChildColumns[k].DefaultValue;
\r
618 case Rule.SetNull: // set the values in the child row to null.
\r
619 if (childRows != null)
\r
621 for (int j = 0; j < childRows.Length; j++)
\r
623 DataRow child = childRows[j];
\r
624 if (childRows[j].RowState != DataRowState.Deleted)
\r
626 // set only the key columns to DBNull
\r
627 for (int k = 0; k < rel.ChildColumns.Length; k++)
\r
628 child.SetNull(rel.ChildColumns[k]);
\r
638 /// Ends the edit occurring on the row.
\r
641 public void EndEdit ()
\r
644 if (rowState == DataRowState.Detached)
\r
646 if (HasVersion (DataRowVersion.Proposed))
\r
648 _table.ChangingDataRow(this, DataRowAction.Change);
\r
649 if (rowState == DataRowState.Unchanged)
\r
650 rowState = DataRowState.Modified;
\r
652 //Calling next method validates UniqueConstraints
\r
656 if (_table.DataSet == null || _table.DataSet.EnforceConstraints)
\r
657 _table.Rows.ValidateDataRowInternal(this);
\r
659 catch (Exception e)
\r
664 // check all child rows.
\r
665 CheckChildRows(DataRowAction.Change);
\r
666 current = proposed;
\r
668 _table.ChangedDataRow(this, DataRowAction.Change);
\r
673 /// Gets the child rows of this DataRow using the specified DataRelation.
\r
675 public DataRow[] GetChildRows (DataRelation relation)
\r
677 return GetChildRows (relation, DataRowVersion.Current);
\r
681 /// Gets the child rows of a DataRow using the specified RelationName of a
\r
684 public DataRow[] GetChildRows (string relationName)
\r
686 return GetChildRows (Table.DataSet.Relations[relationName]);
\r
690 /// Gets the child rows of a DataRow using the specified DataRelation, and
\r
691 /// DataRowVersion.
\r
693 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version)
\r
695 if (relation == null)
\r
696 return new DataRow[0];
\r
698 if (this.Table == null)
\r
699 throw new RowNotInTableException();
\r
701 if (relation.DataSet != this.Table.DataSet)
\r
702 throw new ArgumentException();
\r
704 // TODO: Caching for better preformance
\r
705 ArrayList rows = new ArrayList();
\r
706 DataColumn[] parentColumns = relation.ParentColumns;
\r
707 DataColumn[] childColumns = relation.ChildColumns;
\r
708 int numColumn = parentColumns.Length;
\r
709 if (HasVersion(version))
\r
711 foreach (DataRow row in relation.ChildTable.Rows)
\r
713 bool allColumnsMatch = false;
\r
714 if (row.HasVersion(DataRowVersion.Default))
\r
716 allColumnsMatch = true;
\r
717 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
\r
719 if (!this[parentColumns[columnCnt], version].Equals(
\r
720 row[childColumns[columnCnt], DataRowVersion.Default]))
\r
722 allColumnsMatch = false;
\r
727 if (allColumnsMatch) rows.Add(row);
\r
730 return rows.ToArray(typeof(DataRow)) as DataRow[];
\r
734 /// Gets the child rows of a DataRow using the specified RelationName of a
\r
735 /// DataRelation, and DataRowVersion.
\r
737 public DataRow[] GetChildRows (string relationName, DataRowVersion version)
\r
739 return GetChildRows (Table.DataSet.Relations[relationName], version);
\r
743 /// Gets the error description of the specified DataColumn.
\r
745 public string GetColumnError (DataColumn column)
\r
747 return GetColumnError (_table.Columns.IndexOf(column));
\r
751 /// Gets the error description for the column specified by index.
\r
753 public string GetColumnError (int columnIndex)
\r
755 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
\r
756 throw new IndexOutOfRangeException ();
\r
758 string retVal = columnErrors[columnIndex];
\r
759 if (retVal == null)
\r
760 retVal = string.Empty;
\r
765 /// Gets the error description for the column, specified by name.
\r
767 public string GetColumnError (string columnName)
\r
769 return GetColumnError (_table.Columns.IndexOf(columnName));
\r
773 /// Gets an array of columns that have errors.
\r
775 public DataColumn[] GetColumnsInError ()
\r
777 ArrayList dataColumns = new ArrayList ();
\r
779 for (int i = 0; i < columnErrors.Length; i += 1)
\r
781 if (columnErrors[i] != null && columnErrors[i] != String.Empty)
\r
782 dataColumns.Add (_table.Columns[i]);
\r
785 return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
\r
789 /// Gets the parent row of a DataRow using the specified DataRelation.
\r
791 public DataRow GetParentRow (DataRelation relation)
\r
793 return GetParentRow (relation, DataRowVersion.Current);
\r
797 /// Gets the parent row of a DataRow using the specified RelationName of a
\r
800 public DataRow GetParentRow (string relationName)
\r
802 return GetParentRow (relationName, DataRowVersion.Current);
\r
806 /// Gets the parent row of a DataRow using the specified DataRelation, and
\r
807 /// DataRowVersion.
\r
809 public DataRow GetParentRow (DataRelation relation, DataRowVersion version)
\r
811 DataRow[] rows = GetParentRows(relation, version);
\r
812 if (rows.Length == 0) return null;
\r
817 /// Gets the parent row of a DataRow using the specified RelationName of a
\r
818 /// DataRelation, and DataRowVersion.
\r
820 public DataRow GetParentRow (string relationName, DataRowVersion version)
\r
822 return GetParentRow (Table.DataSet.Relations[relationName], version);
\r
826 /// Gets the parent rows of a DataRow using the specified DataRelation.
\r
828 public DataRow[] GetParentRows (DataRelation relation)
\r
830 return GetParentRows (relation, DataRowVersion.Current);
\r
834 /// Gets the parent rows of a DataRow using the specified RelationName of a
\r
837 public DataRow[] GetParentRows (string relationName)
\r
839 return GetParentRows (relationName, DataRowVersion.Current);
\r
843 /// Gets the parent rows of a DataRow using the specified DataRelation, and
\r
844 /// DataRowVersion.
\r
846 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version)
\r
848 // TODO: Caching for better preformance
\r
849 if (relation == null)
\r
850 return new DataRow[0];
\r
852 if (this.Table == null)
\r
853 throw new RowNotInTableException();
\r
855 if (relation.DataSet != this.Table.DataSet)
\r
856 throw new ArgumentException();
\r
858 ArrayList rows = new ArrayList();
\r
859 DataColumn[] parentColumns = relation.ParentColumns;
\r
860 DataColumn[] childColumns = relation.ChildColumns;
\r
861 int numColumn = parentColumns.Length;
\r
862 if (HasVersion(version))
\r
864 foreach (DataRow row in relation.ParentTable.Rows)
\r
866 bool allColumnsMatch = false;
\r
867 if (row.HasVersion(DataRowVersion.Default))
\r
869 allColumnsMatch = true;
\r
870 for (int columnCnt = 0; columnCnt < numColumn; columnCnt++)
\r
872 if (!this[childColumns[columnCnt], version].Equals(
\r
873 row[parentColumns[columnCnt], DataRowVersion.Default]))
\r
875 allColumnsMatch = false;
\r
880 if (allColumnsMatch) rows.Add(row);
\r
883 return rows.ToArray(typeof(DataRow)) as DataRow[];
\r
887 /// Gets the parent rows of a DataRow using the specified RelationName of a
\r
888 /// DataRelation, and DataRowVersion.
\r
890 public DataRow[] GetParentRows (string relationName, DataRowVersion version)
\r
892 return GetParentRows (Table.DataSet.Relations[relationName], version);
\r
896 /// Gets a value indicating whether a specified version exists.
\r
898 public bool HasVersion (DataRowVersion version)
\r
902 case DataRowVersion.Default:
\r
903 if (rowState == DataRowState.Deleted)
\r
905 if (rowState == DataRowState.Detached)
\r
906 return proposed != null;
\r
908 case DataRowVersion.Proposed:
\r
909 if (rowState == DataRowState.Deleted)
\r
911 return (proposed != null);
\r
912 case DataRowVersion.Current:
\r
913 if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
\r
915 return (current != null);
\r
916 case DataRowVersion.Original:
\r
917 if (rowState == DataRowState.Detached)
\r
919 return (original != null);
\r
925 /// Gets a value indicating whether the specified DataColumn contains a null value.
\r
927 public bool IsNull (DataColumn column)
\r
929 object o = this[column];
\r
930 return (o == null || o == DBNull.Value);
\r
934 /// Gets a value indicating whether the column at the specified index contains a null
\r
937 public bool IsNull (int columnIndex)
\r
939 object o = this[columnIndex];
\r
940 return (o == null || o == DBNull.Value);
\r
944 /// Gets a value indicating whether the named column contains a null value.
\r
946 public bool IsNull (string columnName)
\r
948 object o = this[columnName];
\r
949 return (o == null || o == DBNull.Value);
\r
953 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
\r
954 /// contains a null value.
\r
956 public bool IsNull (DataColumn column, DataRowVersion version)
\r
958 object o = this[column, version];
\r
959 return (o == null || o == DBNull.Value);
\r
963 /// Rejects all changes made to the row since AcceptChanges was last called.
\r
965 public void RejectChanges ()
\r
967 // If original is null, then nothing has happened since AcceptChanges
\r
968 // was last called. We have no "original" to go back to.
\r
969 if (original != null)
\r
971 Array.Copy (original, current, _table.Columns.Count);
\r
973 _table.ChangedDataRow (this, DataRowAction.Rollback);
\r
977 case DataRowState.Added:
\r
978 _table.Rows.Remove (this);
\r
980 case DataRowState.Modified:
\r
981 rowState = DataRowState.Unchanged;
\r
983 case DataRowState.Deleted:
\r
984 rowState = DataRowState.Unchanged;
\r
990 // If rows are just loaded via Xml the original values are null.
\r
991 // So in this case we have to remove all columns.
\r
992 // FIXME: I'm not realy sure, does this break something else, but
\r
995 if ((rowState & DataRowState.Added) > 0)
\r
996 _table.Rows.Remove (this);
\r
1001 /// Sets the error description for a column specified as a DataColumn.
\r
1003 public void SetColumnError (DataColumn column, string error)
\r
1005 SetColumnError (_table.Columns.IndexOf (column), error);
\r
1009 /// Sets the error description for a column specified by index.
\r
1011 public void SetColumnError (int columnIndex, string error)
\r
1013 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
\r
1014 throw new IndexOutOfRangeException ();
\r
1015 columnErrors[columnIndex] = error;
\r
1019 /// Sets the error description for a column specified by name.
\r
1021 public void SetColumnError (string columnName, string error)
\r
1023 SetColumnError (_table.Columns.IndexOf (columnName), error);
\r
1027 /// Sets the value of the specified DataColumn to a null value.
\r
1029 protected void SetNull (DataColumn column)
\r
1031 this[column] = DBNull.Value;
\r
1035 /// Sets the parent row of a DataRow with specified new parent DataRow.
\r
1038 public void SetParentRow (DataRow parentRow)
\r
1040 SetParentRow(parentRow, null);
\r
1044 /// Sets the parent row of a DataRow with specified new parent DataRow and
\r
1048 public void SetParentRow (DataRow parentRow, DataRelation relation)
\r
1050 if (_table == null || parentRow.Table == null)
\r
1051 throw new RowNotInTableException();
\r
1053 if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
\r
1054 throw new ArgumentException();
\r
1057 if (relation == null)
\r
1059 foreach (DataRelation parentRel in _table.ParentRelations)
\r
1061 DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
\r
1062 DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
\r
1064 for (int i = 0; i < parentCols.Length; i++)
\r
1066 if (parentRow == null)
\r
1067 this[childCols[i].Ordinal] = DBNull.Value;
\r
1069 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
\r
1076 DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
\r
1077 DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
\r
1079 for (int i = 0; i < parentCols.Length; i++)
\r
1081 if (parentRow == null)
\r
1082 this[childCols[i].Ordinal] = DBNull.Value;
\r
1084 this[childCols[i].Ordinal] = parentRow[parentCols[i]];
\r
1090 //Copy all values of this DataaRow to the row parameter.
\r
1091 internal void CopyValuesToRow(DataRow row)
\r
1095 throw new ArgumentNullException("row");
\r
1097 throw new ArgumentException("'row' is the same as this object");
\r
1099 DataColumnCollection columns = Table.Columns;
\r
1101 for(int i = 0; i < columns.Count; i++){
\r
1103 string columnName = columns[i].ColumnName;
\r
1104 int index = row.Table.Columns.IndexOf(columnName);
\r
1105 //if a column with the same name exists in both rows copy the values
\r
1107 if (HasVersion(DataRowVersion.Original))
\r
1109 if (row.original == null)
\r
1110 row.original = new object[row.Table.Columns.Count];
\r
1111 row.original[index] = row.SetColumnValue(original[i], index);
\r
1113 if (HasVersion(DataRowVersion.Current))
\r
1115 if (row.current == null)
\r
1116 row.current = new object[row.Table.Columns.Count];
\r
1117 row.current[index] = row.SetColumnValue(current[i], index);
\r
1119 if (HasVersion(DataRowVersion.Proposed))
\r
1121 if (row.proposed == null)
\r
1122 row.proposed = new object[row.Table.Columns.Count];
\r
1123 row.proposed[index] = row.SetColumnValue(proposed[i], index);
\r
1126 //Saving the current value as the column value
\r
1127 row[index] = row.current[index];
\r
1132 row.rowState = RowState;
\r
1133 row.RowError = RowError;
\r
1134 row.columnErrors = columnErrors;
\r
1138 public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
\r
1140 // if a column is added we hava to add an additional value the
\r
1141 // the priginal, current and propoed arrays.
\r
1142 // this scenario can happened in merge operation.
\r
1144 if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
\r
1147 if (current != null)
\r
1149 tmp = new object[current.Length + 1];
\r
1150 Array.Copy (current, tmp, current.Length);
\r
1151 tmp[tmp.Length - 1] = DBNull.Value;
\r
1154 if (proposed != null)
\r
1156 tmp = new object[proposed.Length + 1];
\r
1157 Array.Copy (proposed, tmp, proposed.Length);
\r
1158 tmp[tmp.Length - 1] = DBNull.Value;
\r
1161 if(original != null)
\r
1163 tmp = new object[original.Length + 1];
\r
1164 Array.Copy (original, tmp, original.Length);
\r
1165 tmp[tmp.Length - 1] = DBNull.Value;
\r
1172 internal bool IsRowChanged(DataRowState rowState) {
\r
1173 if((RowState & rowState) != 0)
\r
1176 //we need to find if child rows of this row changed.
\r
1177 //if yes - we should return true
\r
1179 // if the rowState is deleted we should get the original version of the row
\r
1180 // else - we should get the current version of the row.
\r
1181 DataRowVersion version = (rowState == DataRowState.Deleted) ? DataRowVersion.Original : DataRowVersion.Current;
\r
1182 int count = Table.ChildRelations.Count;
\r
1183 for (int i = 0; i < count; i++){
\r
1184 DataRelation rel = Table.ChildRelations[i];
\r
1185 DataRow[] childRows = GetChildRows(rel, version);
\r
1186 for (int j = 0; j < childRows.Length; j++){
\r
1187 if (childRows[j].IsRowChanged(rowState))
\r
1195 internal bool HasParentCollection
\r
1199 return _hasParentCollection;
\r
1203 _hasParentCollection = value;
\r
1207 #endregion // Methods
\r