// Tim Coleman <tim@timcoleman.com>
// Ville Palo <vi64pa@koti.soon.fi>
// Alan Tam Siu Lung <Tam@SiuLung.com>
+// Sureshkumar T <tsureshkumar@novell.com>
//
// (C) Ximian, Inc 2002
// (C) Daniel Morgan 2002, 2003
// Copyright (C) 2002 Tim Coleman
//
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
using System;
+using System.Data.Common;
using System.Collections;
using System.Globalization;
+using System.Xml;
namespace System.Data {
/// <summary>
private DataTable _table;
- private object[] original;
- private object[] proposed;
- private object[] current;
+ internal int _original = -1;
+ internal int _current = -1;
+ internal int _proposed = -1;
- private string[] columnErrors;
+ private ArrayList _columnErrors;
private string rowError;
private DataRowState rowState;
internal int xmlRowID = 0;
private bool editing = false;
private bool _hasParentCollection;
private bool _inChangingEvent;
+ private int _rowId;
+
+ private XmlDataDocument.XmlDataElement mappedElement;
+ internal bool _inExpressionEvaluation = false;
- #endregion
+ #endregion // Fields
#region Constructors
protected internal DataRow (DataRowBuilder builder)
{
_table = builder.Table;
-
- original = null;
-
- proposed = new object[_table.Columns.Count];
- for (int c = 0; c < _table.Columns.Count; c++)
- {
- proposed[c] = DBNull.Value;
+ // Get the row id from the builder.
+ _rowId = builder._rowId;
+
+ _proposed = _table.RecordCache.NewRecord();
+ // Initialise the data columns of the row with the dafault values, if any
+ // TODO : should proposed version be available immediately after record creation ?
+ foreach(DataColumn column in _table.Columns) {
+ column.DataContainer.CopyValue(_table.DefaultValuesRowIndex,_proposed);
}
- columnErrors = new string[_table.Columns.Count];
rowError = String.Empty;
//on first creating a DataRow it is always detached.
rowState = DataRowState.Detached;
- foreach (DataColumn Col in _table.Columns) {
-
- if (Col.AutoIncrement) {
- this [Col] = Col.AutoIncrementValue();
- }
+ ArrayList aiColumns = _table.Columns.AutoIncrmentColumns;
+ foreach (DataColumn dc in aiColumns) {
+ this [dc] = dc.AutoIncrementValue();
}
- _table.Columns.CollectionChanged += new System.ComponentModel.CollectionChangeEventHandler(CollectionChanged);
+
+ // create mapped XmlDataElement
+ DataSet ds = _table.DataSet;
+ if (ds != null && ds._xmlDataDocument != null)
+ mappedElement = new XmlDataDocument.XmlDataElement (this, _table.Prefix, _table.TableName, _table.Namespace, ds._xmlDataDocument);
}
-
- #endregion
+ internal DataRow(DataTable table,int rowId)
+ {
+ _table = table;
+ _rowId = rowId;
+ }
+
+ #endregion // Constructors
#region Properties
+ private ArrayList ColumnErrors
+ {
+ get {
+ if (_columnErrors == null) {
+ _columnErrors = new ArrayList();
+ }
+ return _columnErrors;
+ }
+
+ set {
+ _columnErrors = value;
+ }
+ }
+
/// <summary>
/// Gets a value indicating whether there are errors in a row.
/// </summary>
public bool HasErrors {
- [MonoTODO]
get {
if (RowError != string.Empty)
return true;
- for (int i= 0; i < columnErrors.Length; i++){
- if (columnErrors[i] != null && columnErrors[i] != string.Empty)
+ foreach(String columnError in ColumnErrors) {
+ if (columnError != null && columnError != string.Empty) {
return true;
}
-
+ }
return false;
}
}
throw new IndexOutOfRangeException ();
if (rowState == DataRowState.Deleted)
throw new DeletedRowInaccessibleException ();
+
DataColumn column = _table.Columns[columnIndex];
_table.ChangingDataColumn (this, column, value);
+ if (value == null && column.DataType != typeof(string)) {
+ throw new ArgumentException("Cannot set column " + column.ColumnName + " to be null, Please use DBNull instead");
+ }
+ CheckValue (value, column);
+
bool orginalEditing = editing;
- if (!orginalEditing) BeginEdit ();
- object v = SetColumnValue (value, columnIndex);
- proposed[columnIndex] = v;
- _table.ChangedDataColumn (this, column, v);
- if (!orginalEditing) EndEdit ();
+ if (!orginalEditing) {
+ BeginEdit ();
+ }
+
+ column[_proposed] = value;
+ _table.ChangedDataColumn (this, column, value);
+ if (!orginalEditing) {
+ EndEdit ();
+ }
}
}
/// </summary>
public object this[DataColumn column, DataRowVersion version] {
get {
- int columnIndex = _table.Columns.IndexOf (column);
- if (columnIndex == -1)
+ if (column.Table != Table)
throw new ArgumentException ("The column does not belong to this table.");
+ int columnIndex = column.Ordinal;
return this[columnIndex, version];
}
}
+ /// <summary>
+ /// Sets the index into the container records for the original version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ /// </summary>
+ internal int Original
+ {
+ get { return _original;}
+ set {
+ if (_original == value)
+ return;
+
+ if (_original >= 0
+ && _current != _original
+ && _proposed != _original)
+ Table.RecordCache.DisposeRecord (_original);
+ _original = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets the index into the container records for the proposed version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ internal int Proposed
+ {
+ get { return _proposed;}
+ set {
+ if (_proposed == value)
+ return;
+ if (_proposed >= 0
+ && _proposed != _current
+ && _proposed != _original)
+ Table.RecordCache.DisposeRecord (_proposed);
+ _proposed = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets the index into the container records for the current version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ internal int Current
+ {
+ get { return _current;}
+ set {
+ if (_current == value)
+ return;
+ if (_current >= 0
+ && _current != _original
+ && _current != _proposed)
+ Table.RecordCache.DisposeRecord (_current);
+ _current = value;
+ }
+ }
+
+ /// <summary>
+ /// Set a value for the column into the offset specified by the version.<br>
+ /// If the value is auto increment or null, necessary auto increment value
+ /// or the default value will be used.
+ /// </summary>
+ internal void SetValue (int column, object value, int version)
+ {
+ DataColumn dc = Table.Columns[column];
+
+ if (value == null && ! dc.AutoIncrement) // set default value / auto increment
+ value = dc.DefaultValue;
+
+ Table.ChangingDataColumn (this, dc, value);
+ CheckValue (value, dc);
+ if ( ! dc.AutoIncrement)
+ dc [version] = value;
+ else if (_proposed >= 0 && _proposed != version) // proposed holds the AI
+ dc [version] = dc [_proposed];
+ }
+
/// <summary>
/// Gets the data stored in the column, specified by index and version of the data to
/// retrieve.
if (columnIndex < 0 || columnIndex > _table.Columns.Count)
throw new IndexOutOfRangeException ();
// Accessing deleted rows
- if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
+ if (!_inExpressionEvaluation && rowState == DataRowState.Deleted && version != DataRowVersion.Original)
throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
- // Row not in table
- if (rowState == DataRowState.Detached && version == DataRowVersion.Default)
+
+ DataColumn column = _table.Columns[columnIndex];
+ if (column.Expression != String.Empty) {
+ object o = column.CompiledExpression.Eval (this);
+ return Convert.ChangeType (o, column.DataType);
+ }
+
+ 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.");
- // Non-existent version
- if (!HasVersion (version))
- throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
- switch (version) {
- case DataRowVersion.Default:
- if (editing || rowState == DataRowState.Detached)
- return proposed[columnIndex];
- return current[columnIndex];
- case DataRowVersion.Proposed:
- return proposed[columnIndex];
- case DataRowVersion.Current:
- return current[columnIndex];
- case DataRowVersion.Original:
- return original[columnIndex];
- default:
- throw new ArgumentException ();
+
+ int recordIndex = IndexFromVersion(version);
+
+ if (recordIndex >= 0) {
+ return column[recordIndex];
}
+
+ throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
}
}
-
+
/// <summary>
/// Gets or sets all of the values for this row through an array.
/// </summary>
- [MonoTODO]
public object[] ItemArray {
get {
// row not in table
if (rowState == DataRowState.Deleted)
throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
- return current;
+ object[] items = new object[_table.Columns.Count];
+ foreach(DataColumn column in _table.Columns) {
+ items[column.Ordinal] = column[_current];
+ }
+ return items;
}
set {
if (value.Length > _table.Columns.Count)
if (rowState == DataRowState.Deleted)
throw new DeletedRowInaccessibleException ();
- object[] newItems = new object[_table.Columns.Count];
- object v = null;
- for (int i = 0; i < _table.Columns.Count; i++) {
-
- if (i < value.Length)
- v = value[i];
- else
- v = null;
-
- newItems[i] = SetColumnValue (v, i);
- }
-
bool orginalEditing = editing;
- if (!orginalEditing) BeginEdit ();
- proposed = newItems;
- if (!orginalEditing) EndEdit ();
- }
- }
-
- private object SetColumnValue (object v, int index)
- {
- object newval = null;
- DataColumn col = _table.Columns[index];
-
- if (_hasParentCollection && col.ReadOnly && v != this[index])
- throw new ReadOnlyException ();
-
- if (v == null)
- {
- if(col.DefaultValue != DBNull.Value)
- {
- newval = col.DefaultValue;
- }
- else if(col.AutoIncrement == true)
- {
- newval = this [index];
- }
- else
- {
- if (!col.AllowDBNull)
- {
- //Constraint violations during data load is raise in DataTable EndLoad
- this._nullConstraintViolation = true;
- _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
- }
-
- newval = DBNull.Value;
+ if (!orginalEditing) {
+ BeginEdit ();
}
- }
- else if (v == DBNull.Value)
- {
-
- if (!col.AllowDBNull)
- {
- //Constraint violations during data load is raise in DataTable EndLoad
- this._nullConstraintViolation = true;
- _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
- }
-
- newval = DBNull.Value;
- }
- else
- {
- Type vType = v.GetType(); // data type of value
- Type cType = col.DataType; // column data type
- if (cType != vType)
- {
- TypeCode typeCode = Type.GetTypeCode(cType);
- switch(typeCode) {
- case TypeCode.Boolean :
- v = Convert.ToBoolean (v);
- break;
- case TypeCode.Byte :
- v = Convert.ToByte (v);
- break;
- case TypeCode.Char :
- v = Convert.ToChar (v);
- break;
- case TypeCode.DateTime :
- v = Convert.ToDateTime (v);
- break;
- case TypeCode.Decimal :
- v = Convert.ToDecimal (v);
- break;
- case TypeCode.Double :
- v = Convert.ToDouble (v);
- break;
- case TypeCode.Int16 :
- v = Convert.ToInt16 (v);
- break;
- case TypeCode.Int32 :
- v = Convert.ToInt32 (v);
- break;
- case TypeCode.Int64 :
- v = Convert.ToInt64 (v);
- break;
- case TypeCode.SByte :
- v = Convert.ToSByte (v);
- break;
- case TypeCode.Single :
- v = Convert.ToSingle (v);
- break;
- case TypeCode.String :
- v = Convert.ToString (v);
- break;
- case TypeCode.UInt16 :
- v = Convert.ToUInt16 (v);
- break;
- case TypeCode.UInt32 :
- v = Convert.ToUInt32 (v);
- break;
- case TypeCode.UInt64 :
- v = Convert.ToUInt64 (v);
- break;
- default :
-
- switch(cType.ToString()) {
- case "System.TimeSpan" :
- v = (System.TimeSpan) v;
- break;
- case "System.Type" :
- v = (System.Type) v;
- break;
- case "System.Object" :
- //v = (System.Object) v;
- break;
- default:
- if (!cType.IsArray)
- throw new InvalidCastException("Type not supported.");
- break;
- }
- break;
- }
- vType = v.GetType();
+ object newVal = null;
+ DataColumnChangeEventArgs e = new DataColumnChangeEventArgs();
+ foreach(DataColumn column in _table.Columns) {
+ int i = column.Ordinal;
+ newVal = (i < value.Length) ? value[i] : null;
+
+ e.Initialize(this, column, newVal);
+ _table.RaiseOnColumnChanged(e);
+ CheckValue (e.ProposedValue, column);
+ column[_proposed] = e.ProposedValue;
}
- newval = v;
- if(col.AutoIncrement == true) {
- long inc = Convert.ToInt64(v);
- col.UpdateAutoIncrementValue (inc);
+ if (!orginalEditing) {
+ EndEdit ();
}
}
- col.DataHasBeenSet = true;
- return newval;
}
- /// <summary>
- /// Gets or sets the custom error description for a row.
- /// </summary>
- public string RowError {
- get { return rowError; }
- set { rowError = value; }
+ internal bool IsEditing {
+ get { return editing; }
}
/// <summary>
/// DataRowCollection.
/// </summary>
public DataRowState RowState {
- get { return rowState; }
+ get {
+ return rowState;
+ }
}
+ /// <summary>
+ /// Gets the DataTable for which this row has a schema.
+ /// </summary>
+ public DataTable Table {
+ get {
+ return _table;
+ }
+ }
+
+ /// <summary>
+ /// Gets and sets index of row. This is used from
+ /// XmlDataDocument.
+ // </summary>
+ internal int XmlRowID {
+ get {
+ return xmlRowID;
+ }
+ set {
+ xmlRowID = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets and sets index of row.
+ // </summary>
+ internal int RowID {
+ get {
+ return _rowId;
+ }
+ set {
+ _rowId = value;
+ }
+ }
+
+ #endregion // Properties
+
+ #region Methods
+
//FIXME?: Couldn't find a way to set the RowState when adding the DataRow
//to a Datatable so I added this method. Delete if there is a better way.
internal void AttachRow() {
- current = proposed;
- proposed = null;
+ if (_current >= 0) {
+ Table.RecordCache.DisposeRecord(_current);
+ }
+ _current = _proposed;
+ _proposed = -1;
rowState = DataRowState.Added;
}
//FIXME?: Couldn't find a way to set the RowState when removing the DataRow
//from a Datatable so I added this method. Delete if there is a better way.
internal void DetachRow() {
- proposed = null;
+ if (_proposed >= 0) {
+ _table.RecordCache.DisposeRecord(_proposed);
+ _proposed = -1;
+ }
+ _rowId = -1;
_hasParentCollection = false;
rowState = DataRowState.Detached;
}
+ private void CheckValue (object v, DataColumn col)
+ {
+ if (_hasParentCollection && col.ReadOnly) {
+ throw new ReadOnlyException ();
+ }
+
+ if (v == null || v == DBNull.Value) {
+ if (col.AllowDBNull || col.AutoIncrement || col.DefaultValue != DBNull.Value) {
+ return;
+ }
+
+ //Constraint violations during data load is raise in DataTable EndLoad
+ this._nullConstraintViolation = true;
+ if (this.Table._duringDataLoad) {
+ this.Table._nullConstraintViolationDuringDataLoad = true;
+ }
+ _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";
+
+ }
+ }
+
+ internal void SetValuesFromDataRecord(IDataRecord record, int[] mapping)
+ {
+// bool orginalEditing = editing;
+// if (!orginalEditing) {
+// BeginEdit ();
+// }
+
+ if (!HasVersion(DataRowVersion.Proposed)) {
+ _proposed = Table.RecordCache.NewRecord();
+ }
+
+ try {
+ for(int i=0; i < Table.Columns.Count; i++) {
+ DataColumn column = Table.Columns[i];
+ if (mapping [i] < 0) { // no mapping
+ if (! column.AutoIncrement)
+ column.DataContainer [_proposed] = column.DefaultValue;
+ continue;
+ }
+
+ column.DataContainer.SetItemFromDataRecord(_proposed, record,mapping[i]);
+ if ( column.AutoIncrement ) {
+ column.UpdateAutoIncrementValue(column.DataContainer.GetInt64(_proposed));
+ }
+ }
+ }
+ catch (Exception e){
+ Table.RecordCache.DisposeRecord(_proposed);
+ _proposed = -1;
+ throw e;
+ }
+
+// if (!orginalEditing) {
+// EndEdit ();
+// }
+ }
+
/// <summary>
- /// Gets the DataTable for which this row has a schema.
+ /// Gets or sets the custom error description for a row.
/// </summary>
- public DataTable Table {
- get { return _table; }
+ public string RowError {
+ get {
+ return rowError;
+ }
+ set {
+ rowError = value;
+ }
}
- /// <summary>
- /// Gets and sets index of row. This is used from
- /// XmlDataDocument.
- // </summary>
- internal int XmlRowID {
- get { return xmlRowID; }
- set { xmlRowID = value; }
+ internal int IndexFromVersion(DataRowVersion version)
+ {
+ if (HasVersion(version))
+ {
+ int recordIndex;
+ switch (version) {
+ case DataRowVersion.Default:
+ if (editing || rowState == DataRowState.Detached) {
+ recordIndex = _proposed;
+ }
+ else {
+ recordIndex = _current;
+ }
+ break;
+ case DataRowVersion.Proposed:
+ recordIndex = _proposed;
+ break;
+ case DataRowVersion.Current:
+ recordIndex = _current;
+ break;
+ case DataRowVersion.Original:
+ recordIndex = _original;
+ break;
+ default:
+ throw new ArgumentException ();
+ }
+ return recordIndex;
+ }
+ return -1;
}
- #endregion
+ internal XmlDataDocument.XmlDataElement DataElement {
+ get { return mappedElement; }
+ set { mappedElement = value; }
+ }
- #region Methods
+ internal void SetOriginalValue (string columnName, object val)
+ {
+ DataColumn column = _table.Columns[columnName];
+ _table.ChangingDataColumn (this, column, val);
+
+ if (_original < 0 || _original == _current) {
+ // This really creates a new record version if one does not exist
+ _original = Table.RecordCache.NewRecord();
+ }
+ CheckValue (val, column);
+ column[_original] = val;
+ rowState = DataRowState.Modified;
+ }
/// <summary>
/// Commits all the changes made to this row since the last time AcceptChanges was
{
EndEdit(); // in case it hasn't been called
switch (rowState) {
+ case DataRowState.Unchanged:
+ return;
case DataRowState.Added:
case DataRowState.Modified:
rowState = DataRowState.Unchanged;
throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
}
// Accept from detached
- if (original == null)
- original = new object[_table.Columns.Count];
- Array.Copy (current, original, _table.Columns.Count);
+ if (_original != _current)
+ Original = Current;
}
/// <summary>
/// Begins an edit operation on a DataRow object.
/// </summary>
- [MonoTODO]
public void BeginEdit ()
{
+ if (_inChangingEvent)
+ throw new InRowChangingEventException("Cannot call BeginEdit inside an OnRowChanging event.");
if (rowState == DataRowState.Deleted)
throw new DeletedRowInaccessibleException ();
if (!HasVersion (DataRowVersion.Proposed)) {
- proposed = new object[_table.Columns.Count];
- Array.Copy (current, proposed, current.Length);
+ _proposed = Table.RecordCache.NewRecord();
+ foreach(DataColumn column in Table.Columns) {
+ column.DataContainer.CopyValue(_current,_proposed);
+ }
}
- //TODO: Suspend validation
+ // setting editing to true stops validations on the row
editing = true;
}
/// <summary>
/// Cancels the current edit on the row.
/// </summary>
- [MonoTODO]
public void CancelEdit ()
{
+ if (_inChangingEvent)
+ throw new InRowChangingEventException("Cannot call CancelEdit inside an OnRowChanging event.");
editing = false;
- //TODO: Events
if (HasVersion (DataRowVersion.Proposed)) {
- proposed = null;
- if (rowState == DataRowState.Modified)
+ Table.RecordCache.DisposeRecord(_proposed);
+ _proposed = -1;
+ if (rowState == DataRowState.Modified) {
rowState = DataRowState.Unchanged;
+ }
}
}
public void ClearErrors ()
{
rowError = String.Empty;
- columnErrors = new String[_table.Columns.Count];
+ ColumnErrors.Clear();
}
/// <summary>
/// Deletes the DataRow.
/// </summary>
- [MonoTODO]
public void Delete ()
{
_table.DeletingDataRow(this, DataRowAction.Delete);
switch (rowState) {
case DataRowState.Added:
+ // check what to do with child rows
+ CheckChildRows(DataRowAction.Delete);
+ _table.DeleteRowFromIndexes (this);
Table.Rows.RemoveInternal (this);
+
+ // if row was in Added state we move it to Detached.
+ DetachRow();
break;
case DataRowState.Deleted:
- break;
+ break;
default:
// check what to do with child rows
CheckChildRows(DataRowAction.Delete);
+ _table.DeleteRowFromIndexes (this);
rowState = DataRowState.Deleted;
break;
}
- _table.DeletedDataRow(this, DataRowAction.Delete);
+ _table.DeletedDataRow(this, DataRowAction.Delete);
}
// check the child rows of this row before deleting the row.
private void CheckChildRows(DataRowAction action)
{
- // in this method we find the row that this row is in a reltion with them.
+ // in this method we find the row that this row is in a relation with them.
// in shortly we find all child rows of this row.
// then we function according to the DeleteRule of the foriegnkey.
{
foreach (DataTable table in _table.DataSet.Tables)
{
- // loop on all constraints of the table.
- ConstraintCollection constraintsCollection = table.Constraints;
- for (int i = 0; i < constraintsCollection.Count; i++)
+ // loop on all ForeignKeyConstrain of the table.
+ foreach (ForeignKeyConstraint fk in table.Constraints.ForeignKeyConstraints)
{
- ForeignKeyConstraint fk = null;
- if (constraintsCollection[i] is ForeignKeyConstraint)
+ if (fk.RelatedTable == _table)
{
- fk = (ForeignKeyConstraint)constraintsCollection[i];
- if (fk.RelatedTable == _table)
- {
- //we create a dummy relation because we do not want to duplicate code of GetChild().
- // we use the dummy relation to find child rows.
- DataRelation rel = new DataRelation("dummy", fk.RelatedColumns, fk.Columns, false);
- Rule rule;
- if (action == DataRowAction.Delete)
- rule = fk.DeleteRule;
- else
- rule = fk.UpdateRule;
- CheckChildRows(rel, action, rule);
- }
+ Rule rule;
+ if (action == DataRowAction.Delete)
+ rule = fk.DeleteRule;
+ else
+ rule = fk.UpdateRule;
+ CheckChildRows(fk, action, rule);
}
}
}
}
}
- private void CheckChildRows(DataRelation rel, DataRowAction action, Rule rule)
+ private void CheckChildRows(ForeignKeyConstraint fkc, DataRowAction action, Rule rule)
{
- DataRow[] childRows = GetChildRows(rel);
+ DataRow[] childRows = GetChildRows(fkc, DataRowVersion.Default);
switch (rule)
{
case Rule.Cascade: // delete or change all relted rows.
{
// change only the values in the key columns
// set the childcolumn value to the new parent row value
- for (int k = 0; k < rel.ChildColumns.Length; k++)
- childRows[j][rel.ChildColumns[k]] = this[rel.ParentColumns[k], DataRowVersion.Proposed];
+ for (int k = 0; k < fkc.Columns.Length; k++)
+ childRows[j][fkc.Columns[k]] = this[fkc.RelatedColumns[k], DataRowVersion.Proposed];
}
}
}
{
if (childRows[j].RowState != DataRowState.Deleted)
{
- string changeStr = "Cannot change this row because constraints are enforced on relation " + rel.RelationName +", and changing this row will strand child rows.";
- string delStr = "Cannot delete this row because constraints are enforced on relation " + rel.RelationName +", and deleting this row will strand child rows.";
+ string changeStr = "Cannot change this row because constraints are enforced on relation " + fkc.ConstraintName +", and changing this row will strand child rows.";
+ string delStr = "Cannot delete this row because constraints are enforced on relation " + fkc.ConstraintName +", and deleting this row will strand child rows.";
string message = action == DataRowAction.Delete ? delStr : changeStr;
throw new InvalidConstraintException(message);
}
}
break;
case Rule.SetDefault: // set the values in the child rows to the defult value of the columns.
- if (childRows != null)
- {
- for (int j = 0; j < childRows.Length; j++)
- {
- DataRow child = childRows[j];
- if (childRows[j].RowState != DataRowState.Deleted)
- {
- //set only the key columns to default
- for (int k = 0; k < rel.ChildColumns.Length; k++)
- child[rel.ChildColumns[k]] = rel.ChildColumns[k].DefaultValue;
+ if (childRows != null && childRows.Length > 0) {
+ int defaultValuesRowIndex = childRows[0].Table.DefaultValuesRowIndex;
+ foreach(DataRow childRow in childRows) {
+ if (childRow.RowState != DataRowState.Deleted) {
+ int defaultIdx = childRow.IndexFromVersion(DataRowVersion.Default);
+ foreach(DataColumn column in fkc.Columns) {
+ column.DataContainer.CopyValue(defaultValuesRowIndex,defaultIdx);
+ }
}
}
}
if (childRows[j].RowState != DataRowState.Deleted)
{
// set only the key columns to DBNull
- for (int k = 0; k < rel.ChildColumns.Length; k++)
- child.SetNull(rel.ChildColumns[k]);
+ for (int k = 0; k < fkc.Columns.Length; k++)
+ child.SetNull(fkc.Columns[k]);
}
}
}
/// <summary>
/// Ends the edit occurring on the row.
/// </summary>
- [MonoTODO]
public void EndEdit ()
{
if (_inChangingEvent)
editing = false;
return;
}
-
+
+ CheckReadOnlyStatus();
if (HasVersion (DataRowVersion.Proposed))
{
_inChangingEvent = true;
catch (Exception e)
{
editing = false;
- proposed = null;
+ Table.RecordCache.DisposeRecord(_proposed);
+ _proposed = -1;
throw e;
}
- // check all child rows.
- CheckChildRows(DataRowAction.Change);
- current = proposed;
- proposed = null;
+
+ // Now we are going to check all child rows of current row.
+ // In the case the cascade is true the child rows will look up for
+ // parent row. since lookup in index is always on current,
+ // we have to move proposed version of current row to current
+ // in the case of check child row failure we are rolling
+ // current row state back.
+ int backup = _current;
+ _current = _proposed;
+ bool editing_backup = editing;
editing = false;
+ try {
+ // check all child rows.
+ CheckChildRows(DataRowAction.Change);
+ _proposed = -1;
+ if (_original != backup) {
+ Table.RecordCache.DisposeRecord(backup);
+ }
+ }
+ catch (Exception ex) {
+ // if check child rows failed - rollback to previous state
+ // i.e. restore proposed and current versions
+ _proposed = _current;
+ _current = backup;
+ editing = editing_backup;
+ // since we failed - propagate an exception
+ throw ex;
+ }
_table.ChangedDataRow(this, DataRowAction.Change);
}
}
/// </summary>
public DataRow[] GetChildRows (DataRelation relation)
{
- return GetChildRows (relation, DataRowVersion.Current);
+ return GetChildRows (relation, DataRowVersion.Default);
}
/// <summary>
if (relation == null)
return new DataRow[0];
- if (this.Table == null || RowState == DataRowState.Detached)
+ //if (this.Table == null || RowState == DataRowState.Detached)
+ if (this.Table == null)
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 (relation.DataSet != this.Table.DataSet)
throw new ArgumentException();
- // TODO: Caching for better preformance
+ if (_table != relation.ParentTable)
+ throw new InvalidConstraintException ("GetChildRow requires a row whose Table is " + relation.ParentTable + ", but the specified row's table is " + _table);
+
+ if (relation.ChildKeyConstraint != null)
+ return GetChildRows (relation.ChildKeyConstraint, version);
+
ArrayList rows = new ArrayList();
DataColumn[] parentColumns = relation.ParentColumns;
DataColumn[] childColumns = relation.ChildColumns;
int numColumn = parentColumns.Length;
- if (HasVersion(version))
+ if (HasVersion(version))
{
+ object[] vals = new object[parentColumns.Length];
+ for (int i = 0; i < vals.Length; i++)
+ vals[i] = this[parentColumns[i], version];
+
foreach (DataRow row in relation.ChildTable.Rows)
{
bool allColumnsMatch = false;
allColumnsMatch = true;
for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt)
{
- if (!this[parentColumns[columnCnt], version].Equals(
+ if (!vals[columnCnt].Equals(
row[childColumns[columnCnt], DataRowVersion.Default]))
{
allColumnsMatch = false;
}
if (allColumnsMatch) rows.Add(row);
}
- }
- return rows.ToArray(typeof(DataRow)) as DataRow[];
+ }else
+ throw new VersionNotFoundException("There is no " + version + " data to accces.");
+
+ DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
+ rows.CopyTo(result, 0);
+ return result;
}
/// <summary>
return GetChildRows (Table.DataSet.Relations[relationName], version);
}
+ private DataRow[] GetChildRows (ForeignKeyConstraint fkc, DataRowVersion version)
+ {
+ ArrayList rows = new ArrayList();
+ DataColumn[] parentColumns = fkc.RelatedColumns;
+ DataColumn[] childColumns = fkc.Columns;
+ int numColumn = parentColumns.Length;
+ if (HasVersion(version)) {
+ Index index = fkc.Index;
+ if (index != null) {
+ // get the child rows from the index
+ Node[] childNodes = index.FindAllSimple (parentColumns, IndexFromVersion(version));
+ for (int i = 0; i < childNodes.Length; i++) {
+ rows.Add (childNodes[i].Row);
+ }
+ }
+ else { // if there is no index we search manualy.
+ int curIndex = IndexFromVersion(DataRowVersion.Default);
+ int tmpRecord = fkc.Table.RecordCache.NewRecord();
+
+ try {
+ for (int i = 0; i < numColumn; i++) {
+ // according to MSDN: the DataType value for both columns must be identical.
+ childColumns[i].DataContainer.CopyValue(parentColumns[i].DataContainer, curIndex, tmpRecord);
+ }
+
+ foreach (DataRow row in fkc.Table.Rows) {
+ bool allColumnsMatch = false;
+ if (row.HasVersion(DataRowVersion.Default)) {
+ allColumnsMatch = true;
+ int childIndex = row.IndexFromVersion(DataRowVersion.Default);
+ for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) {
+ if (childColumns[columnCnt].DataContainer.CompareValues(childIndex, tmpRecord) != 0) {
+ allColumnsMatch = false;
+ break;
+ }
+ }
+ }
+ if (allColumnsMatch) {
+ rows.Add(row);
+ }
+ }
+ }
+ finally {
+ fkc.Table.RecordCache.DisposeRecord(tmpRecord);
+ }
+ }
+ }else
+ throw new VersionNotFoundException("There is no " + version + " data to accces.");
+
+ DataRow[] result = fkc.Table.NewRowArray(rows.Count);
+ rows.CopyTo(result, 0);
+ return result;
+ }
+
/// <summary>
/// Gets the error description of the specified DataColumn.
/// </summary>
/// </summary>
public string GetColumnError (int columnIndex)
{
- if (columnIndex < 0 || columnIndex >= columnErrors.Length)
+ if (columnIndex < 0 || columnIndex >= Table.Columns.Count)
throw new IndexOutOfRangeException ();
- string retVal = columnErrors[columnIndex];
- if (retVal == null)
- retVal = string.Empty;
- return retVal;
+ string retVal = null;
+ if (columnIndex < ColumnErrors.Count) {
+ retVal = (String) ColumnErrors[columnIndex];
+ }
+ return (retVal != null) ? retVal : String.Empty;
}
/// <summary>
{
ArrayList dataColumns = new ArrayList ();
- for (int i = 0; i < columnErrors.Length; i += 1)
- {
- if (columnErrors[i] != null && columnErrors[i] != String.Empty)
- dataColumns.Add (_table.Columns[i]);
+ int columnOrdinal = 0;
+ foreach(String columnError in ColumnErrors) {
+ if (columnError != null && columnError != String.Empty) {
+ dataColumns.Add (_table.Columns[columnOrdinal]);
+ }
+ columnOrdinal++;
}
return (DataColumn[])(dataColumns.ToArray (typeof(DataColumn)));
/// </summary>
public DataRow GetParentRow (DataRelation relation)
{
- return GetParentRow (relation, DataRowVersion.Current);
+ return GetParentRow (relation, DataRowVersion.Default);
}
/// <summary>
/// </summary>
public DataRow GetParentRow (string relationName)
{
- return GetParentRow (relationName, DataRowVersion.Current);
+ return GetParentRow (relationName, DataRowVersion.Default);
}
/// <summary>
/// </summary>
public DataRow[] GetParentRows (DataRelation relation)
{
- return GetParentRows (relation, DataRowVersion.Current);
+ return GetParentRows (relation, DataRowVersion.Default);
}
/// <summary>
/// </summary>
public DataRow[] GetParentRows (string relationName)
{
- return GetParentRows (relationName, DataRowVersion.Current);
+ return GetParentRows (relationName, DataRowVersion.Default);
}
/// <summary>
if (relation == null)
return new DataRow[0];
- if (this.Table == null || RowState == DataRowState.Detached)
+ if (this.Table == null)
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 (relation.DataSet != this.Table.DataSet)
throw new ArgumentException();
+ if (_table != relation.ChildTable)
+ throw new InvalidConstraintException ("GetParentRows requires a row whose Table is " + relation.ChildTable + ", but the specified row's table is " + _table);
+
ArrayList rows = new ArrayList();
DataColumn[] parentColumns = relation.ParentColumns;
DataColumn[] childColumns = relation.ChildColumns;
int numColumn = parentColumns.Length;
- if (HasVersion(version))
- {
- foreach (DataRow row in relation.ParentTable.Rows)
- {
- bool allColumnsMatch = false;
- if (row.HasVersion(DataRowVersion.Default))
- {
- allColumnsMatch = true;
- for (int columnCnt = 0; columnCnt < numColumn; columnCnt++)
- {
- if (!this[childColumns[columnCnt], version].Equals(
- row[parentColumns[columnCnt], DataRowVersion.Default]))
- {
- allColumnsMatch = false;
- break;
+ if (HasVersion(version)) {
+ Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
+ if (indx != null &&
+ (Table == null || Table.DataSet == null ||
+ Table.DataSet.EnforceConstraints)) { // get the child rows from the index
+ Node[] childNodes = indx.FindAllSimple(childColumns, IndexFromVersion(version));
+ for (int i = 0; i < childNodes.Length; i++) {
+ rows.Add (childNodes[i].Row);
+ }
+ }
+ else { // no index so we have to search manualy.
+ int curIndex = IndexFromVersion(DataRowVersion.Default);
+ int tmpRecord = relation.ParentTable.RecordCache.NewRecord();
+ try {
+ for (int i = 0; i < numColumn; i++) {
+ // according to MSDN: the DataType value for both columns must be identical.
+ parentColumns[i].DataContainer.CopyValue(childColumns[i].DataContainer, curIndex, tmpRecord);
+ }
+
+ foreach (DataRow row in relation.ParentTable.Rows) {
+ bool allColumnsMatch = false;
+ if (row.HasVersion(DataRowVersion.Default)) {
+ allColumnsMatch = true;
+ int parentIndex = row.IndexFromVersion(DataRowVersion.Default);
+ for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) {
+ if (parentColumns[columnCnt].DataContainer.CompareValues(parentIndex, tmpRecord) != 0) {
+ allColumnsMatch = false;
+ break;
+ }
+ }
+ }
+ if (allColumnsMatch) {
+ rows.Add(row);
}
}
}
- if (allColumnsMatch) rows.Add(row);
+ finally {
+ relation.ParentTable.RecordCache.DisposeRecord(tmpRecord);
+ }
}
- }
- return rows.ToArray(typeof(DataRow)) as DataRow[];
+ }else
+ throw new VersionNotFoundException("There is no " + version + " data to accces.");
+
+ DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
+ rows.CopyTo(result, 0);
+ return result;
}
/// <summary>
/// </summary>
public bool HasVersion (DataRowVersion version)
{
- switch (version)
- {
- case DataRowVersion.Default:
- if (rowState == DataRowState.Deleted)
- return false;
- if (rowState == DataRowState.Detached)
- return proposed != null;
- return true;
- case DataRowVersion.Proposed:
- if (rowState == DataRowState.Deleted)
- return false;
- return (proposed != null);
- case DataRowVersion.Current:
- if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
- return false;
- return (current != null);
- case DataRowVersion.Original:
- if (rowState == DataRowState.Detached)
- return false;
- return (original != null);
- }
+ switch (version) {
+ case DataRowVersion.Default:
+ if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+ return false;
+ if (rowState == DataRowState.Detached)
+ return _proposed >= 0;
+ return true;
+ case DataRowVersion.Proposed:
+ if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+ return false;
+ return _proposed >= 0;
+ case DataRowVersion.Current:
+ if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
+ return false;
+ return _current >= 0;
+ case DataRowVersion.Original:
+ if (rowState == DataRowState.Detached)
+ return false;
+ return _original >= 0;
+ }
return false;
}
/// </summary>
public bool IsNull (DataColumn column)
{
- object o = this[column];
- return (o == null || o == DBNull.Value);
+ return IsNull(column, DataRowVersion.Default);
}
/// <summary>
/// </summary>
public bool IsNull (int columnIndex)
{
- object o = this[columnIndex];
- return (o == null || o == DBNull.Value);
+ return IsNull(Table.Columns[columnIndex]);
}
/// <summary>
/// </summary>
public bool IsNull (string columnName)
{
- object o = this[columnName];
- return (o == null || o == DBNull.Value);
+ return IsNull(Table.Columns[columnName]);
}
/// <summary>
/// </summary>
public bool IsNull (DataColumn column, DataRowVersion version)
{
- object o = this[column, version];
- return (o == null || o == DBNull.Value);
+ return column.DataContainer.IsNull(IndexFromVersion(version));
+ }
+
+ /// <summary>
+ /// Returns a value indicating whether all of the row columns specified contain a null value.
+ /// </summary>
+ internal bool IsNullColumns(DataColumn[] columns)
+ {
+ bool allNull = true;
+ for (int i = 0; i < columns.Length; i++)
+ {
+ if (!IsNull(columns[i]))
+ {
+ allNull = false;
+ break;
+ }
+ }
+ return allNull;
}
/// <summary>
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 (original != null)
- {
- Array.Copy (original, current, _table.Columns.Count);
+ if (HasVersion(DataRowVersion.Original)) {
+ if (_current >= 0 ) {
+ Table.RecordCache.DisposeRecord(_current);
+ }
+ _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);
rowState = DataRowState.Unchanged;
break;
case DataRowState.Deleted:
rowState = DataRowState.Unchanged;
+ if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
+ _table.Rows.ValidateDataRowInternal(this);
break;
}
// 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();
+ }
}
}
/// </summary>
public void SetColumnError (int columnIndex, string error)
{
- if (columnIndex < 0 || columnIndex >= columnErrors.Length)
+ if (columnIndex < 0 || columnIndex >= Table.Columns.Count)
throw new IndexOutOfRangeException ();
- columnErrors[columnIndex] = error;
+
+ while(ColumnErrors.Count < columnIndex) {
+ ColumnErrors.Add(null);
+ }
+ ColumnErrors.Add(error);
}
/// <summary>
/// <summary>
/// Sets the parent row of a DataRow with specified new parent DataRow.
/// </summary>
- [MonoTODO]
public void SetParentRow (DataRow parentRow)
{
SetParentRow(parentRow, null);
/// Sets the parent row of a DataRow with specified new parent DataRow and
/// DataRelation.
/// </summary>
- [MonoTODO]
public void SetParentRow (DataRow parentRow, DataRelation relation)
{
- if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
+ if (_table == null || parentRow.Table == null)
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 (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
{
foreach (DataRelation parentRel in _table.ParentRelations)
{
- DataColumn[] childCols = parentRel.ChildKeyConstraint.Columns;
- DataColumn[] parentCols = parentRel.ChildKeyConstraint.RelatedColumns;
+ DataColumn[] childCols = parentRel.ChildColumns;
+ DataColumn[] parentCols = parentRel.ParentColumns;
for (int i = 0; i < parentCols.Length; i++)
{
}
else
{
- DataColumn[] childCols = relation.ChildKeyConstraint.Columns;
- DataColumn[] parentCols = relation.ChildKeyConstraint.RelatedColumns;
+ DataColumn[] childCols = relation.ChildColumns;
+ DataColumn[] parentCols = relation.ParentColumns;
for (int i = 0; i < parentCols.Length; i++)
{
//Copy all values of this DataaRow to the row parameter.
internal void CopyValuesToRow(DataRow row)
{
-
if (row == null)
throw new ArgumentNullException("row");
if (row == this)
throw new ArgumentException("'row' is the same as this object");
- DataColumnCollection columns = Table.Columns;
-
- for(int i = 0; i < columns.Count; i++){
-
- string columnName = columns[i].ColumnName;
- int index = row.Table.Columns.IndexOf(columnName);
+ 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(index != -1) {
- if (HasVersion(DataRowVersion.Original))
- {
- if (row.original == null)
- row.original = new object[row.Table.Columns.Count];
- row.original[index] = row.SetColumnValue(original[i], index);
+ 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;
}
- if (HasVersion(DataRowVersion.Current))
- {
- if (row.current == null)
- row.current = new object[row.Table.Columns.Count];
- row.current[index] = row.SetColumnValue(current[i], index);
+ if (HasVersion(DataRowVersion.Current)) {
+ if (row._current < 0) {
+ row._current = row.Table.RecordCache.NewRecord();
+ }
+ object val = column[_current];
+ row.CheckValue(val, targetColumn);
+ targetColumn[row._current] = val;
}
- if (HasVersion(DataRowVersion.Proposed))
- {
- if (row.proposed == null)
- row.proposed = new object[row.Table.Columns.Count];
- row.proposed[index] = row.SetColumnValue(proposed[i], index);
+ 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;
}
//Saving the current value as the column value
- row[index] = row.current[index];
-
+ object defaultVal = column [IndexFromVersion (DataRowVersion.Default)];
+ row [index] = defaultVal;
}
}
-
- row.rowState = RowState;
- row.RowError = RowError;
- row.columnErrors = columnErrors;
+ CopyState(row);
}
-
- public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
+ // Copy row state - rowState and errors
+ internal void CopyState(DataRow row)
{
- // if a column is added we hava to add an additional value the
- // the priginal, current and propoed arrays.
- // this scenario can happened in merge operation.
-
- if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
- {
- object[] tmp;
- if (current != null)
- {
- tmp = new object[current.Length + 1];
- Array.Copy (current, tmp, current.Length);
- tmp[tmp.Length - 1] = DBNull.Value;
- current = tmp;
- }
- if (proposed != null)
- {
- tmp = new object[proposed.Length + 1];
- Array.Copy (proposed, tmp, proposed.Length);
- tmp[tmp.Length - 1] = DBNull.Value;
- proposed = tmp;
- }
- if(original != null)
- {
- tmp = new object[original.Length + 1];
- Array.Copy (original, tmp, original.Length);
- tmp[tmp.Length - 1] = DBNull.Value;
- original = tmp;
- }
-
- }
+ row.rowState = RowState;
+ row.RowError = RowError;
+ row.ColumnErrors = (ArrayList)ColumnErrors.Clone();
}
internal bool IsRowChanged(DataRowState rowState) {
internal void CheckNullConstraints()
{
- if (_nullConstraintViolation)
- {
- for (int i = 0; i < proposed.Length; i++)
- {
- if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
- throw new NoNullAllowedException(_nullConstraintMessage);
+ if (_nullConstraintViolation) {
+ if (HasVersion(DataRowVersion.Proposed)) {
+ foreach(DataColumn column in Table.Columns) {
+ if (IsNull(column) && !column.AllowDBNull) {
+ throw new NoNullAllowedException(_nullConstraintMessage);
+ }
+ }
}
_nullConstraintViolation = false;
}
}
-
+
+ internal void CheckReadOnlyStatus()
+ {
+ if (HasVersion(DataRowVersion.Proposed)) {
+ int defaultIdx = IndexFromVersion(DataRowVersion.Default);
+ foreach(DataColumn column in Table.Columns) {
+ if ((column.DataContainer.CompareValues(defaultIdx,_proposed) != 0) && column.ReadOnly) {
+ throw new ReadOnlyException();
+ }
+ }
+ }
+ }
+
#endregion // Methods
+
+#if NET_2_0
+ /// <summary>
+ /// This method loads a given value into the existing row affecting versions,
+ /// 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)
+ {
+ 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;
+ }
+
+ if (loadOption == LoadOption.OverwriteChanges
+ || (loadOption == LoadOption.PreserveChanges
+ && rowState == DataRowState.Unchanged)) {
+ Original = temp;
+ if (editing)
+ Proposed = temp;
+ else
+ Current = temp;
+ rowState = DataRowState.Unchanged;
+ action = DataRowAction.ChangeCurrentAndOriginal;
+ return;
+ }
+
+ if (loadOption == LoadOption.PreserveChanges) {
+ if (rowState != DataRowState.Deleted) {
+ Original = temp;
+ rowState = DataRowState.Modified;
+ action = 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 (not_used)
+ Table.RecordCache.DisposeRecord (temp);
+ }
+#endif // NET_2_0
}