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 private bool editing = false;
46 /// This member supports the .NET Framework infrastructure and is not intended to be
47 /// used directly from your code.
49 protected internal DataRow (DataRowBuilder builder)
51 _table = builder.Table;
54 current = new object[_table.Columns.Count];
55 // initialize to DBNull.Value
56 for (int c = 0; c < _table.Columns.Count; c++) {
57 current[c] = DBNull.Value;
59 proposed = new object[_table.Columns.Count];
60 Array.Copy (current, proposed, _table.Columns.Count);
62 columnErrors = new string[_table.Columns.Count];
63 rowError = String.Empty;
65 //on first creating a DataRow it is always detached.
66 rowState = DataRowState.Detached;
68 foreach (DataColumn Col in _table.Columns) {
70 if (Col.AutoIncrement) {
71 this [Col] = Col.AutoIncrementValue();
81 /// Gets a value indicating whether there are errors in a row.
83 public bool HasErrors {
86 throw new NotImplementedException ();
91 /// Gets or sets the data stored in the column specified by name.
93 public object this[string columnName] {
94 get { return this[columnName, DataRowVersion.Default]; }
96 int columnIndex = _table.Columns.IndexOf (columnName);
97 if (columnIndex == -1)
98 throw new IndexOutOfRangeException ();
99 this[columnIndex] = value;
104 /// Gets or sets the data stored in specified DataColumn
106 public object this[DataColumn column] {
107 get { return this[column, DataRowVersion.Default]; }
109 int columnIndex = _table.Columns.IndexOf (column);
110 if (columnIndex == -1)
111 throw new ArgumentException ("The column does not belong to this table.");
112 this[columnIndex] = value;
117 /// Gets or sets the data stored in column specified by index.
119 public object this[int columnIndex] {
120 get { return this[columnIndex, DataRowVersion.Default]; }
122 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
123 throw new IndexOutOfRangeException ();
124 if (rowState == DataRowState.Deleted)
125 throw new DeletedRowInaccessibleException ();
126 DataColumn column = _table.Columns[columnIndex];
127 _table.ChangingDataColumn (this, column, value);
129 bool orginalEditing = editing;
130 if (!orginalEditing) BeginEdit ();
131 object v = SetColumnValue (value, columnIndex);
132 proposed[columnIndex] = v;
133 _table.ChangedDataColumn (this, column, v);
134 if (!orginalEditing) EndEdit ();
139 /// Gets the specified version of data stored in the named column.
141 public object this[string columnName, DataRowVersion version] {
143 int columnIndex = _table.Columns.IndexOf (columnName);
144 if (columnIndex == -1)
145 throw new IndexOutOfRangeException ();
146 return this[columnIndex, version];
151 /// Gets the specified version of data stored in the specified DataColumn.
153 public object this[DataColumn column, DataRowVersion version] {
155 int columnIndex = _table.Columns.IndexOf (column);
156 if (columnIndex == -1)
157 throw new ArgumentException ("The column does not belong to this table.");
158 return this[columnIndex, version];
163 /// Gets the data stored in the column, specified by index and version of the data to
166 public object this[int columnIndex, DataRowVersion version] {
168 if (columnIndex < 0 || columnIndex > _table.Columns.Count)
169 throw new IndexOutOfRangeException ();
170 // Non-existent version
171 if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !HasVersion (version))
172 throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
173 // Accessing deleted rows
174 if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
175 throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
177 case DataRowVersion.Default:
178 if (editing || rowState == DataRowState.Detached)
179 return proposed[columnIndex];
180 return current[columnIndex];
181 case DataRowVersion.Proposed:
182 return proposed[columnIndex];
183 case DataRowVersion.Current:
184 return current[columnIndex];
185 case DataRowVersion.Original:
186 return original[columnIndex];
188 throw new ArgumentException ();
194 /// Gets or sets all of the values for this row through an array.
197 public object[] ItemArray {
202 if (value.Length > _table.Columns.Count)
203 throw new ArgumentException ();
205 if (rowState == DataRowState.Deleted)
206 throw new DeletedRowInaccessibleException ();
208 object[] newItems = new object[_table.Columns.Count];
210 for (int i = 0; i < _table.Columns.Count; i++) {
212 if (i < value.Length)
217 newItems[i] = SetColumnValue (v, i);
220 bool orginalEditing = editing;
221 if (!orginalEditing) BeginEdit ();
223 if (!orginalEditing) EndEdit ();
227 private object SetColumnValue (object v, int index)
229 object newval = null;
230 DataColumn col = _table.Columns[index];
232 if (col.ReadOnly && v != this[index])
233 throw new ReadOnlyException ();
236 if(col.DefaultValue != DBNull.Value) {
237 newval = col.DefaultValue;
239 else if(col.AutoIncrement == true) {
240 newval = this [index];
243 if (!col.AllowDBNull)
244 throw new NoNullAllowedException ();
245 newval = DBNull.Value;
248 else if (v == DBNull.Value) {
249 if (!col.AllowDBNull)
250 throw new NoNullAllowedException ();
252 newval = DBNull.Value;
256 Type vType = v.GetType(); // data type of value
257 Type cType = col.DataType; // column data type
258 if (cType != vType) {
259 TypeCode typeCode = Type.GetTypeCode(cType);
261 case TypeCode.Boolean :
262 v = Convert.ToBoolean (v);
265 v = Convert.ToByte (v);
268 v = Convert.ToChar (v);
270 case TypeCode.DateTime :
271 v = Convert.ToDateTime (v);
273 case TypeCode.Decimal :
274 v = Convert.ToDecimal (v);
276 case TypeCode.Double :
277 v = Convert.ToDouble (v);
279 case TypeCode.Int16 :
280 v = Convert.ToInt16 (v);
282 case TypeCode.Int32 :
283 v = Convert.ToInt32 (v);
285 case TypeCode.Int64 :
286 v = Convert.ToInt64 (v);
288 case TypeCode.SByte :
289 v = Convert.ToSByte (v);
291 case TypeCode.Single :
292 v = Convert.ToSingle (v);
294 case TypeCode.String :
295 v = Convert.ToString (v);
297 case TypeCode.UInt16 :
298 v = Convert.ToUInt16 (v);
300 case TypeCode.UInt32 :
301 v = Convert.ToUInt32 (v);
303 case TypeCode.UInt64 :
304 v = Convert.ToUInt64 (v);
307 switch(cType.ToString()) {
308 case "System.TimeSpan" :
309 v = (System.TimeSpan) v;
314 case "System.Object" :
315 //v = (System.Object) v;
318 // FIXME: is exception correct?
319 throw new InvalidCastException("Type not supported.");
326 if(col.AutoIncrement == true) {
327 long inc = Convert.ToInt64(v);
328 col.UpdateAutoIncrementValue (inc);
331 col.DataHasBeenSet = true;
336 /// Gets or sets the custom error description for a row.
338 public string RowError {
339 get { return rowError; }
340 set { rowError = value; }
344 /// Gets the current state of the row in regards to its relationship to the
345 /// DataRowCollection.
347 public DataRowState RowState {
348 get { return rowState; }
351 //FIXME?: Couldn't find a way to set the RowState when adding the DataRow
352 //to a Datatable so I added this method. Delete if there is a better way.
353 internal void AttachRow() {
356 rowState = DataRowState.Added;
359 //FIXME?: Couldn't find a way to set the RowState when removing the DataRow
360 //from a Datatable so I added this method. Delete if there is a better way.
361 internal void DetachRow() {
363 rowState = DataRowState.Detached;
367 /// Gets the DataTable for which this row has a schema.
369 public DataTable Table {
370 get { return _table; }
374 /// Gets and sets index of row. This is used from
377 internal int XmlRowID {
378 get { return xmlRowID; }
379 set { xmlRowID = value; }
387 /// Commits all the changes made to this row since the last time AcceptChanges was
390 public void AcceptChanges ()
392 EndEdit(); // in case it hasn't been called
394 case DataRowState.Added:
395 case DataRowState.Detached:
396 case DataRowState.Modified:
397 rowState = DataRowState.Unchanged;
399 case DataRowState.Deleted:
400 _table.Rows.Remove (this);
403 // Accept from detached
404 if (original == null)
405 original = new object[_table.Columns.Count];
406 Array.Copy (current, original, _table.Columns.Count);
410 /// Begins an edit operation on a DataRow object.
413 public void BeginEdit ()
415 if (rowState == DataRowState.Deleted)
416 throw new DeletedRowInaccessibleException ();
417 if (!HasVersion (DataRowVersion.Proposed)) {
418 proposed = new object[_table.Columns.Count];
419 Array.Copy (current, proposed, current.Length);
421 //TODO: Suspend validation
426 /// Cancels the current edit on the row.
429 public void CancelEdit ()
432 if (HasVersion (DataRowVersion.Proposed)) {
434 if (rowState == DataRowState.Modified)
435 rowState = DataRowState.Unchanged;
440 /// Clears the errors for the row, including the RowError and errors set with
443 public void ClearErrors ()
445 rowError = String.Empty;
446 columnErrors = new String[_table.Columns.Count];
450 /// Deletes the DataRow.
453 public void Delete ()
456 case DataRowState.Added:
457 Table.Rows.Remove (this);
459 case DataRowState.Deleted:
460 throw new DeletedRowInaccessibleException ();
462 //TODO: Events, Constraints
463 rowState = DataRowState.Deleted;
469 /// Ends the edit occurring on the row.
472 public void EndEdit ()
475 if (rowState == DataRowState.Detached)
477 if (HasVersion (DataRowVersion.Proposed))
479 if (rowState == DataRowState.Unchanged)
480 rowState = DataRowState.Modified;
482 //Calling next method validates UniqueConstraints
484 _table.Rows.ValidateDataRowInternal(this);
491 /// Gets the child rows of this DataRow using the specified DataRelation.
493 public DataRow[] GetChildRows (DataRelation relation)
495 return GetChildRows (relation, DataRowVersion.Current);
499 /// Gets the child rows of a DataRow using the specified RelationName of a
502 public DataRow[] GetChildRows (string relationName)
504 return GetChildRows (Table.DataSet.Relations[relationName]);
508 /// Gets the child rows of a DataRow using the specified DataRelation, and
511 public DataRow[] GetChildRows (DataRelation relation, DataRowVersion version)
513 // TODO: Caching for better preformance
514 ArrayList rows = new ArrayList();
515 DataColumn[] parentColumns = relation.ParentColumns;
516 DataColumn[] childColumns = relation.ChildColumns;
517 int numColumn = parentColumns.Length;
518 foreach (DataRow row in relation.ChildTable.Rows) {
519 bool allColumnsMatch = true;
520 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
521 if (!this[parentColumns[columnCnt], version].Equals(
522 row[childColumns[columnCnt], version])) {
523 allColumnsMatch = false;
527 if (allColumnsMatch) rows.Add(row);
529 return rows.ToArray(typeof(DataRow)) as DataRow[];
533 /// Gets the child rows of a DataRow using the specified RelationName of a
534 /// DataRelation, and DataRowVersion.
536 public DataRow[] GetChildRows (string relationName, DataRowVersion version)
538 return GetChildRows (Table.DataSet.Relations[relationName], version);
542 /// Gets the error description of the specified DataColumn.
544 public string GetColumnError (DataColumn column)
546 return GetColumnError (_table.Columns.IndexOf(column));
550 /// Gets the error description for the column specified by index.
552 public string GetColumnError (int columnIndex)
554 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
555 throw new IndexOutOfRangeException ();
557 return columnErrors[columnIndex];
561 /// Gets the error description for the column, specified by name.
563 public string GetColumnError (string columnName)
565 return GetColumnError (_table.Columns.IndexOf(columnName));
569 /// Gets an array of columns that have errors.
571 public DataColumn[] GetColumnsInError ()
573 ArrayList dataColumns = new ArrayList ();
575 for (int i = 0; i < columnErrors.Length; i += 1)
577 if (columnErrors[i] != String.Empty)
578 dataColumns.Add (_table.Columns[i]);
581 return (DataColumn[])(dataColumns.ToArray ());
585 /// Gets the parent row of a DataRow using the specified DataRelation.
587 public DataRow GetParentRow (DataRelation relation)
589 return GetParentRow (relation, DataRowVersion.Current);
593 /// Gets the parent row of a DataRow using the specified RelationName of a
596 public DataRow GetParentRow (string relationName)
598 return GetParentRow (relationName, DataRowVersion.Current);
602 /// Gets the parent row of a DataRow using the specified DataRelation, and
605 public DataRow GetParentRow (DataRelation relation, DataRowVersion version)
607 DataRow[] rows = GetParentRows(relation, version);
608 if (rows.Length == 0) return null;
613 /// Gets the parent row of a DataRow using the specified RelationName of a
614 /// DataRelation, and DataRowVersion.
616 public DataRow GetParentRow (string relationName, DataRowVersion version)
618 return GetParentRow (Table.DataSet.Relations[relationName], version);
622 /// Gets the parent rows of a DataRow using the specified DataRelation.
624 public DataRow[] GetParentRows (DataRelation relation)
626 return GetParentRows (relation, DataRowVersion.Current);
630 /// Gets the parent rows of a DataRow using the specified RelationName of a
633 public DataRow[] GetParentRows (string relationName)
635 return GetParentRows (relationName, DataRowVersion.Current);
639 /// Gets the parent rows of a DataRow using the specified DataRelation, and
642 public DataRow[] GetParentRows (DataRelation relation, DataRowVersion version)
644 // TODO: Caching for better preformance
645 ArrayList rows = new ArrayList();
646 DataColumn[] parentColumns = relation.ParentColumns;
647 DataColumn[] childColumns = relation.ChildColumns;
648 int numColumn = parentColumns.Length;
649 foreach (DataRow row in relation.ParentTable.Rows) {
650 bool allColumnsMatch = true;
651 for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
652 if (!this[parentColumns[columnCnt], version].Equals(
653 row[childColumns[columnCnt], version])) {
654 allColumnsMatch = false;
658 if (allColumnsMatch) rows.Add(row);
660 return rows.ToArray(typeof(DataRow)) as DataRow[];
664 /// Gets the parent rows of a DataRow using the specified RelationName of a
665 /// DataRelation, and DataRowVersion.
667 public DataRow[] GetParentRows (string relationName, DataRowVersion version)
669 return GetParentRows (Table.DataSet.Relations[relationName], version);
673 /// Gets a value indicating whether a specified version exists.
675 public bool HasVersion (DataRowVersion version)
679 case DataRowVersion.Default:
681 case DataRowVersion.Proposed:
682 return (proposed != null);
683 case DataRowVersion.Current:
684 return (current != null);
685 case DataRowVersion.Original:
686 return (original != null);
692 /// Gets a value indicating whether the specified DataColumn contains a null value.
694 public bool IsNull (DataColumn column)
696 return (this[column] == null);
700 /// Gets a value indicating whether the column at the specified index contains a null
703 public bool IsNull (int columnIndex)
705 return (this[columnIndex] == null);
709 /// Gets a value indicating whether the named column contains a null value.
711 public bool IsNull (string columnName)
713 return (this[columnName] == null);
717 /// Gets a value indicating whether the specified DataColumn and DataRowVersion
718 /// contains a null value.
720 public bool IsNull (DataColumn column, DataRowVersion version)
722 return (this[column, version] == null);
726 /// Rejects all changes made to the row since AcceptChanges was last called.
728 public void RejectChanges ()
730 // If original is null, then nothing has happened since AcceptChanges
731 // was last called. We have no "original" to go back to.
732 if (original != null)
734 Array.Copy (original, current, _table.Columns.Count);
736 _table.ChangedDataRow (this, DataRowAction.Rollback);
740 case DataRowState.Added:
741 _table.Rows.Remove (this);
743 case DataRowState.Modified:
744 rowState = DataRowState.Unchanged;
746 case DataRowState.Deleted:
747 rowState = DataRowState.Unchanged;
753 // If rows are just loaded via Xml the original values are null.
754 // So in this case we have to remove all columns.
755 // FIXME: I'm not realy sure, does this break something else, but
758 if ((rowState & DataRowState.Added) > 0)
759 _table.Rows.Remove (this);
764 /// Sets the error description for a column specified as a DataColumn.
766 public void SetColumnError (DataColumn column, string error)
768 SetColumnError (_table.Columns.IndexOf (column), error);
772 /// Sets the error description for a column specified by index.
774 public void SetColumnError (int columnIndex, string error)
776 if (columnIndex < 0 || columnIndex >= columnErrors.Length)
777 throw new IndexOutOfRangeException ();
778 columnErrors[columnIndex] = error;
782 /// Sets the error description for a column specified by name.
784 public void SetColumnError (string columnName, string error)
786 SetColumnError (_table.Columns.IndexOf (columnName), error);
790 /// Sets the value of the specified DataColumn to a null value.
792 protected void SetNull (DataColumn column)
794 this[column] = DBNull.Value;
798 /// Sets the parent row of a DataRow with specified new parent DataRow.
801 public void SetParentRow (DataRow parentRow)
803 throw new NotImplementedException ();
807 /// Sets the parent row of a DataRow with specified new parent DataRow and
811 public void SetParentRow (DataRow parentRow, DataRelation relation)
813 throw new NotImplementedException ();
817 #endregion // Methods