using System.Collections;
using System.Globalization;
using System.Xml;
-
-using System.Data.Common;
+#if NET_2_0
+using System.ComponentModel;
+#endif
namespace System.Data {
/// <summary>
/// Represents a row of data in a DataTable.
/// </summary>
+#if !NET_2_0
[Serializable]
+#endif
public class DataRow
{
#region Fields
}
CheckValue (value, column);
-
bool orginalEditing = Proposed >= 0;
if (!orginalEditing) {
BeginEdit ();
get {
if (columnIndex < 0 || columnIndex > _table.Columns.Count)
throw new IndexOutOfRangeException ();
- // Accessing deleted rows
- if (!_inExpressionEvaluation && RowState == DataRowState.Deleted && version != DataRowVersion.Original)
- throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
DataColumn column = _table.Columns[columnIndex];
int recordIndex = IndexFromVersion(version);
return column[recordIndex];
}
- if (RowState == DataRowState.Detached && version == DataRowVersion.Default && Proposed < 0)
- 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.");
-
return column[recordIndex];
}
}
/// </summary>
public object[] ItemArray {
get {
- // row not in table
- if (RowState == DataRowState.Detached)
- 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.");
// Accessing deleted rows
if (RowState == DataRowState.Deleted)
throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
+
+ int index = 0;
+ if (RowState == DataRowState.Detached)
+ // Check if datarow is removed from the table.
+ if (Proposed < 0)
+ 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.");
+ else
+ index = Proposed;
+ else
+ index = Current;
object[] items = new object[_table.Columns.Count];
- foreach(DataColumn column in _table.Columns) {
- items[column.Ordinal] = column[Current];
- }
+
+ foreach(DataColumn column in _table.Columns)
+ items[column.Ordinal] = column[index];
return items;
}
set {
public DataRowState RowState {
get {
//return rowState;
- if ((Original == -1) && (Current == -1))
- {
- return DataRowState.Detached;
- }
- if (Original == Current)
- {
- return DataRowState.Unchanged;
- }
- if (Original == -1)
- {
- return DataRowState.Added;
- }
- if (Current == -1)
- {
- return DataRowState.Deleted;
- }
- return DataRowState.Modified;
+ if ((Original == -1) && (Current == -1))
+ return DataRowState.Detached;
+ if (Original == Current)
+ return DataRowState.Unchanged;
+ if (Original == -1)
+ return DataRowState.Added;
+ if (Current == -1)
+ return DataRowState.Deleted;
+ return DataRowState.Modified;
}
}
_table.ChangingDataRow (this, DataRowAction.Commit);
CheckChildRows(DataRowAction.Commit);
switch (RowState) {
- case DataRowState.Unchanged:
+ case DataRowState.Unchanged:
return;
case DataRowState.Added:
case DataRowState.Modified:
- if (Original >= 0) {
- Table.RecordCache.DisposeRecord(Original);
- }
- Original = Current;
+ int original = Original;
+ DataRowState oldState = RowState;
+ if (Original >= 0) {
+ Table.RecordCache.DisposeRecord(Original);
+ }
+ Original = Current;
+ foreach (Index index in Table.Indexes)
+ index.Update(this, original, DataRowVersion.Original, oldState);
break;
case DataRowState.Deleted:
+ Table.DeleteRowFromIndexes(this);
_table.Rows.RemoveInternal (this);
DetachRow();
break;
/// <summary>
/// Begins an edit operation on a DataRow object.
/// </summary>
+#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+#endif
public void BeginEdit ()
{
if (_inChangingEvent)
/// <summary>
/// Cancels the current edit on the row.
/// </summary>
+#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+#endif
public void CancelEdit ()
{
if (_inChangingEvent) {
}
if (HasVersion (DataRowVersion.Proposed)) {
+ int oldRecord = Proposed;
+ DataRowState oldState = RowState;
Table.RecordCache.DisposeRecord(Proposed);
Proposed = -1;
+
+ foreach(Index index in Table.Indexes)
+ index.Update(this,oldRecord, DataRowVersion.Proposed, oldState);
}
}
DetachRow();
break;
case DataRowState.Deleted:
- break;
+ case DataRowState.Detached:
+ break;
default:
// check what to do with child rows
CheckChildRows(DataRowAction.Delete);
- _table.DeleteRowFromIndexes (this);
break;
}
if (Current >= 0) {
+ int current = Current;
+ DataRowState oldState = RowState;
if (Current != Original) {
_table.RecordCache.DisposeRecord(Current);
}
Current = -1;
+ foreach(Index index in Table.Indexes)
+ index.Update(this, current, DataRowVersion.Current, oldState);
}
_table.DeletedDataRow(this, DataRowAction.Delete);
}
case DataRowAction.Change: {
// change only the values in the key columns
// set the childcolumn value to the new parent row value
- for (int k = 0; k < fkc.Columns.Length; k++)
- childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
+ for (int k = 0; k < fkc.Columns.Length; k++)
+ if (!fkc.RelatedColumns [k].DataContainer [Original].Equals (fkc.RelatedColumns [k].DataContainer [Proposed]))
+ childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
break;
}
case DataRowAction.Rollback: {
if (childRows[j].RowState != DataRowState.Unchanged)
childRows[j].RejectChanges ();
-
break;
}
}
/// <summary>
/// Ends the edit occurring on the row.
/// </summary>
+#if NET_2_0
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+#endif
public void EndEdit ()
{
if (_inChangingEvent)
_inChangingEvent = false;
}
- //Calling next method validates UniqueConstraints
- //and ForeignKeys.
- bool rowValidated = false;
- try
- {
- if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad) {
- _table.Rows.ValidateDataRowInternal(this);
- rowValidated = true;
- }
- }
- catch (Exception e)
- {
- Table.RecordCache.DisposeRecord(Proposed);
- Proposed = -1;
- throw e;
- }
-
- CheckChildRows(DataRowAction.Change);
- if (Original != Current) {
- Table.RecordCache.DisposeRecord(Current);
- }
+ DataRowState oldState = RowState;
+ int oldRecord = Current;
Current = Proposed;
Proposed = -1;
- if (!rowValidated) {
- // keep indexes updated even if there was no need to validate row
+ if (!Table._duringDataLoad) {
foreach(Index index in Table.Indexes) {
- index.Update(this,Current); //FIXME: why Current ?!
+ index.Update(this,oldRecord, DataRowVersion.Current, oldState);
}
}
+ try {
+ AssertConstraints();
+
+ // restore previous state to let the cascade update to find the rows
+ Proposed = Current;
+ Current = oldRecord;
+
+ CheckChildRows(DataRowAction.Change);
+
+ // apply new state
+ Current = Proposed;
+ Proposed = -1;
+ }
+ catch {
+ int proposed = Proposed >= 0 ? Proposed : Current;
+ Current = oldRecord;
+ if (!Table._duringDataLoad) {
+ foreach(Index index in Table.Indexes) {
+ index.Update(this,proposed, DataRowVersion.Current, RowState);
+ }
+ }
+ throw;
+ }
+
+ if (Original != oldRecord) {
+ Table.RecordCache.DisposeRecord(oldRecord);
+ }
+
// Note : row state must not be changed before all the job on indexes finished,
// since the indexes works with recods rather than with rows and the decision
// which of row records to choose depends on row state.
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.");
// If original is null, then nothing has happened since AcceptChanges
// was last called. We have no "original" to go back to.
- if (HasVersion(DataRowVersion.Original)) {
- if (Current >= 0 && Current != Original) {
- Table.RecordCache.DisposeRecord(Current);
- }
- CheckChildRows(DataRowAction.Rollback);
+
+ _table.ChangedDataRow (this, DataRowAction.Rollback);
+ CancelEdit ();
+ //TODO : Need to Verify the constraints..
+ switch (RowState) {
+ case DataRowState.Added:
+ _table.DeleteRowFromIndexes (this);
+ _table.Rows.RemoveInternal (this);
+ DetachRow ();
+ break;
+ case DataRowState.Modified:
+ Table.RecordCache.DisposeRecord (Current);
+ CheckChildRows (DataRowAction.Rollback);
+ Table.DeleteRowFromIndexes(this);
Current = Original;
-
- _table.ChangedDataRow (this, DataRowAction.Rollback);
- CancelEdit ();
- switch (RowState)
- {
- case DataRowState.Added:
- _table.DeleteRowFromIndexes (this);
- _table.Rows.RemoveInternal (this);
- break;
- case DataRowState.Modified:
- if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
- _table.Rows.ValidateDataRowInternal(this);
- break;
- case DataRowState.Deleted:
- if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
- _table.Rows.ValidateDataRowInternal(this);
- break;
- }
-
- }
- else {
- // If rows are just loaded via Xml the original values are null.
- // So in this case we have to remove all columns.
- // FIXME: I'm not realy sure, does this break something else, but
- // if so: FIXME ;)
-
- if ((RowState & DataRowState.Added) > 0)
- {
- _table.DeleteRowFromIndexes (this);
- _table.Rows.RemoveInternal (this);
- // if row was in Added state we move it to Detached.
- DetachRow();
- }
+ break;
+ case DataRowState.Deleted:
+ CheckChildRows (DataRowAction.Rollback);
+ Table.DeleteRowFromIndexes(this);
+ Current = Original;
+ break;
}
}
if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
throw new ArgumentException();
+
+ if (RowState == DataRowState.Detached && !HasVersion(DataRowVersion.Default)) {
+ // the row should have default data to access, i.e. we can do this for the newly created row, but not for the row once deleted from the table
+ 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.");
+ }
BeginEdit();
if (row == this)
throw new ArgumentException("'row' is the same as this object");
+ // create target records if missing.
+ if (HasVersion(DataRowVersion.Original)) {
+ if (row.Original < 0)
+ row.Original = row.Table.RecordCache.NewRecord();
+ else if (row.Original == row.Current) {
+ row.Original = row.Table.RecordCache.NewRecord();
+ row.Table.RecordCache.CopyRecord (row.Table, row.Current, row.Original);
+ }
+ } else {
+ if (row.Original > 0) {
+ if (row.Original != row.Current)
+ row.Table.RecordCache.DisposeRecord(row.Original);
+ row.Original = -1;
+ }
+ }
+
+ if (HasVersion(DataRowVersion.Current)) {
+ if (Current == Original) {
+ if (row.Current >= 0)
+ row.Table.RecordCache.DisposeRecord(row.Current);
+ row.Current = row.Original;
+ }else {
+ if (row.Current < 0)
+ row.Current = row.Table.RecordCache.NewRecord();
+ }
+ } else {
+ if (row.Current > 0) {
+ row.Table.RecordCache.DisposeRecord(row.Current);
+ row.Current = -1;
+ }
+ }
+
+ if (HasVersion(DataRowVersion.Proposed)) {
+ if (row.Proposed < 0)
+ row.Proposed = row.Table.RecordCache.NewRecord();
+ } else {
+ if (row.Proposed > 0) {
+ row.Table.RecordCache.DisposeRecord(row.Proposed);
+ row.Proposed = -1;
+ }
+ }
+
+ // copy source record values to target records
foreach(DataColumn column in Table.Columns) {
DataColumn targetColumn = row.Table.Columns[column.ColumnName];
//if a column with the same name exists in both rows copy the values
if(targetColumn != null) {
- int index = targetColumn.Ordinal;
if (HasVersion(DataRowVersion.Original)) {
- if (row.Original < 0) {
- row.Original = row.Table.RecordCache.NewRecord();
- }
object val = column[Original];
row.CheckValue(val, targetColumn);
targetColumn[row.Original] = val;
}
- else {
- if (row.Original > 0) {
- row.Table.RecordCache.DisposeRecord(row.Original);
- row.Original = -1;
- }
- }
- if (HasVersion(DataRowVersion.Current)) {
- if (row.Current < 0) {
- row.Current = row.Table.RecordCache.NewRecord();
- }
+ if (HasVersion(DataRowVersion.Current)
+ && Current != Original) {
object val = column[Current];
row.CheckValue(val, targetColumn);
targetColumn[row.Current] = val;
}
- else {
- if (row.Current > 0) {
- row.Table.RecordCache.DisposeRecord(row.Current);
- row.Current = -1;
- }
- }
if (HasVersion(DataRowVersion.Proposed)) {
- if (row.Proposed < 0) {
- row.Proposed = row.Table.RecordCache.NewRecord();
- }
object val = column[row.Proposed];
row.CheckValue(val, targetColumn);
targetColumn[row.Proposed] = val;
}
- else {
- if (row.Proposed > 0) {
- row.Table.RecordCache.DisposeRecord(row.Proposed);
- row.Proposed = -1;
- }
+ }
+ }
+ if (HasErrors) {
+ CopyErrors(row);
+ }
+ }
+
+ //Merge all values of this DataRow to the row parameter according to merge rules.
+ internal void MergeValuesToRow(DataRow row, bool preserveChanges)
+ {
+ if (row == null)
+ throw new ArgumentNullException("row");
+ if (row == this)
+ throw new ArgumentException("'row' is the same as this object");
+
+ // Original values are anyway copied
+ if (HasVersion(DataRowVersion.Original)) {
+ if (row.Original < 0)
+ row.Original = row.Table.RecordCache.NewRecord();
+ else if (row.Original == row.Current
+ && !(Original == Current && ! preserveChanges)) {
+ row.Original = row.Table.RecordCache.NewRecord();
+ row.Table.RecordCache.CopyRecord (row.Table, row.Current, row.Original);
+ }
+ } else {
+ if (row.Original == row.Current) { // if target has same current, better create new original
+ row.Original = row.Table.RecordCache.NewRecord();
+ row.Table.RecordCache.CopyRecord (row.Table, row.Current, row.Original);
+ }
+ }
+
+ // if i have current, push all
+ if (HasVersion(DataRowVersion.Current)) {
+ if (! preserveChanges && row.Current < 0)
+ row.Current = row.Table.RecordCache.NewRecord();
+ } else {
+ if (row.Current > 0 && ! preserveChanges) {
+ row.Table.RecordCache.DisposeRecord(row.Current);
+ row.Current = -1;
+ }
+ }
+
+ // copy source record values to target records
+ foreach(DataColumn column in Table.Columns) {
+ DataColumn targetColumn = row.Table.Columns[column.ColumnName];
+ //if a column with the same name exists in both rows copy the values
+ if(targetColumn != null) {
+ if (HasVersion(DataRowVersion.Original)) {
+ object val = column[Original];
+ row.CheckValue(val, targetColumn);
+ targetColumn[row.Original] = val;
+ }
+
+ if (HasVersion(DataRowVersion.Current)
+ && !preserveChanges) {
+ object val = column[Current];
+ row.CheckValue(val, targetColumn);
+ targetColumn[row.Current] = val;
}
}
}
}
}
+ internal void Validate() {
+ Table.AddRowToIndexes(this);
+ AssertConstraints();
+ }
+
+ void AssertConstraints() {
+ if (Table == null || Table._duringDataLoad)
+ return;
+
+ if (Table.DataSet != null && !Table.DataSet.EnforceConstraints)
+ return;
+ foreach(DataColumn column in Table.Columns) {
+ if (!column.AllowDBNull && IsNull(column)) {
+ throw new NoNullAllowedException(_nullConstraintMessage);
+ }
+ }
+
+ foreach(Constraint constraint in Table.Constraints) {
+ try {
+ constraint.AssertConstraint(this);
+ }
+ catch(Exception e) {
+ Table.DeleteRowFromIndexes(this);
+ throw e;
+ }
+ }
+ }
+
internal void CheckNullConstraints()
{
if (_nullConstraintViolation) {
/// state based on the LoadOption. The matrix of changes for this method are as
/// mentioned in the DataTable.Load (IDataReader, LoadOption) method.
/// </summary>
- [MonoTODO ("Raise necessary Events")]
- internal void Load (object [] values, LoadOption loadOption, bool is_new)
+ internal void Load (object [] values, LoadOption loadOption)
{
- DataRowAction action = DataRowAction.Change;
-
- int temp = Table.RecordCache.NewRecord ();
- for (int i = 0 ; i < Table.Columns.Count; i++)
- SetValue (i, values [i], temp);
-
- if (is_new) { // new row
- if (editing || RowState == DataRowState.Detached)
- Proposed = temp;
- else
- Current = temp;
- return;
- }
+ Index index = null;
+ int temp = -1;
if (loadOption == LoadOption.OverwriteChanges
|| (loadOption == LoadOption.PreserveChanges
- && rowState == DataRowState.Unchanged)) {
+ && RowState == DataRowState.Unchanged)) {
+ Table.ChangingDataRow (this, DataRowAction.ChangeCurrentAndOriginal);
+ temp = Table.CreateRecord (values);
+ Table.DeleteRowFromIndexes(this);
+ if (HasVersion (DataRowVersion.Original) && Current != Original)
+ Table.RecordCache.DisposeRecord (Original);
Original = temp;
- if (editing)
- Proposed = temp;
- else
- Current = temp;
- rowState = DataRowState.Unchanged;
- action = DataRowAction.ChangeCurrentAndOriginal;
+
+ if (HasVersion (DataRowVersion.Current))
+ Table.RecordCache.DisposeRecord (Current);
+ Current = temp;
+ Table.AddRowToIndexes(this);
+ Table.ChangedDataRow (this, DataRowAction.ChangeCurrentAndOriginal);
return;
}
if (loadOption == LoadOption.PreserveChanges) {
- if (rowState != DataRowState.Deleted) {
- Original = temp;
- rowState = DataRowState.Modified;
- action = DataRowAction.ChangeOriginal;
- }
+ Table.ChangingDataRow (this, DataRowAction.ChangeOriginal);
+ temp = Table.CreateRecord (values);
+ if (HasVersion (DataRowVersion.Original) && Current != Original)
+ Table.RecordCache.DisposeRecord (Original);
+ Original = temp;
+ Table.ChangedDataRow (this, DataRowAction.ChangeOriginal);
return;
}
- bool not_used = true;
// Upsert
- if (rowState != DataRowState.Deleted) {
- int index = editing ? _proposed : _current;
- if (! RecordCache.CompareRecords (Table, index, temp)) {
- if (editing)
- Proposed = temp;
- else
- Current = temp;
- not_used = false;
- if (rowState == DataRowState.Unchanged)
- rowState = DataRowState.Modified;
- }
+ if (RowState != DataRowState.Deleted) {
+ int rindex = HasVersion (DataRowVersion.Proposed) ? Proposed : Current;
+ temp = Table.CreateRecord (values);
+ if (RowState == DataRowState.Added
+ || Table.CompareRecords (rindex, temp) != 0) {
+ Table.ChangingDataRow (this, DataRowAction.Change);
+ Table.DeleteRowFromIndexes(this);
+ if (HasVersion (DataRowVersion.Proposed)) {
+ Table.RecordCache.DisposeRecord (Proposed);
+ Proposed = -1;
+ }
+
+ if (Original != Current)
+ Table.RecordCache.DisposeRecord (Current);
+ Current = temp;
+ Table.AddRowToIndexes(this);
+ Table.ChangedDataRow (this, DataRowAction.Change);
+ } else {
+ Table.ChangingDataRow (this, DataRowAction.Nothing);
+ Table.RecordCache.DisposeRecord (temp);
+ Table.ChangedDataRow (this, DataRowAction.Nothing);
+ }
}
-
- if (not_used)
- Table.RecordCache.DisposeRecord (temp);
+
}
#endif // NET_2_0
}