In Test/System.Data:
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
index 42f7a85377fa92c573a6e399a713d6d4afc4627e..cbc95c861ae1b5836f75fb6277d032ffd4e338ab 100644 (file)
@@ -7,15 +7,41 @@
 //   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>
@@ -28,11 +54,11 @@ namespace System.Data {
 
                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;
@@ -41,8 +67,12 @@ namespace System.Data {
                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
 
@@ -53,49 +83,69 @@ namespace System.Data {
                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;
                        }
                }
@@ -138,16 +188,26 @@ namespace System.Data {
                                        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 ();
+                               }
                        }
                }
 
@@ -168,13 +228,89 @@ namespace System.Data {
                /// </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.
@@ -184,35 +320,31 @@ namespace System.Data {
                                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
@@ -222,7 +354,11 @@ namespace System.Data {
                                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)
@@ -231,157 +367,29 @@ namespace System.Data {
                                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>
@@ -389,44 +397,191 @@ namespace System.Data {
                /// 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
@@ -436,6 +591,8 @@ namespace System.Data {
                {
                        EndEdit(); // in case it hasn't been called
                        switch (rowState) {
+                               case DataRowState.Unchanged:
+                                       return;
                        case DataRowState.Added:
                        case DataRowState.Modified:
                                rowState = DataRowState.Unchanged;
@@ -448,39 +605,43 @@ namespace System.Data {
                                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;
+                               }
                        }
                }
 
@@ -491,36 +652,42 @@ namespace System.Data {
                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.
 
@@ -532,35 +699,26 @@ namespace System.Data {
                        {
                                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.
@@ -579,8 +737,8 @@ namespace System.Data {
                                                        {
                                                                // 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];
                                                        }
                                                }
                                        }
@@ -592,8 +750,8 @@ namespace System.Data {
                                                {
                                                        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);
                                                        }
@@ -601,16 +759,14 @@ namespace System.Data {
                                        }
                                        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);
+                                                               }
                                                        }
                                                }
                                        }
@@ -624,8 +780,8 @@ namespace System.Data {
                                                        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]);
                                                        }
                                                }
                                        }
@@ -637,7 +793,6 @@ namespace System.Data {
                /// <summary>
                /// Ends the edit occurring on the row.
                /// </summary>
-               [MonoTODO]
                public void EndEdit () 
                {
                        if (_inChangingEvent)
@@ -647,7 +802,8 @@ namespace System.Data {
                                editing = false;
                                return;
                        }
-
+                       
+                       CheckReadOnlyStatus();
                        if (HasVersion (DataRowVersion.Proposed))
                        {
                                _inChangingEvent = true;
@@ -672,14 +828,38 @@ namespace System.Data {
                                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);
                        }
                }
@@ -689,7 +869,7 @@ namespace System.Data {
                /// </summary>
                public DataRow[] GetChildRows (DataRelation relation) 
                {
-                       return GetChildRows (relation, DataRowVersion.Current);
+                       return GetChildRows (relation, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -710,19 +890,29 @@ namespace System.Data {
                        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;
@@ -731,7 +921,7 @@ namespace System.Data {
                                                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;
@@ -741,8 +931,12 @@ namespace System.Data {
                                        }
                                        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>
@@ -754,6 +948,60 @@ namespace System.Data {
                        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>
@@ -767,13 +1015,14 @@ namespace System.Data {
                /// </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>
@@ -791,10 +1040,12 @@ namespace System.Data {
                {
                        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)));
@@ -805,7 +1056,7 @@ namespace System.Data {
                /// </summary>
                public DataRow GetParentRow (DataRelation relation) 
                {
-                       return GetParentRow (relation, DataRowVersion.Current);
+                       return GetParentRow (relation, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -814,7 +1065,7 @@ namespace System.Data {
                /// </summary>
                public DataRow GetParentRow (string relationName) 
                {
-                       return GetParentRow (relationName, DataRowVersion.Current);
+                       return GetParentRow (relationName, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -842,7 +1093,7 @@ namespace System.Data {
                /// </summary>
                public DataRow[] GetParentRows (DataRelation relation) 
                {
-                       return GetParentRows (relation, DataRowVersion.Current);
+                       return GetParentRows (relation, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -851,7 +1102,7 @@ namespace System.Data {
                /// </summary>
                public DataRow[] GetParentRows (string relationName) 
                {
-                       return GetParentRows (relationName, DataRowVersion.Current);
+                       return GetParentRows (relationName, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -864,38 +1115,65 @@ namespace System.Data {
                        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>
@@ -912,27 +1190,26 @@ namespace System.Data {
                /// </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;
                }
 
@@ -941,8 +1218,7 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (DataColumn column) 
                {
-                       object o = this[column];
-                       return (o == null || o == DBNull.Value);
+                       return IsNull(column, DataRowVersion.Default);
                }
 
                /// <summary>
@@ -951,8 +1227,7 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (int columnIndex) 
                {
-                       object o = this[columnIndex];
-                       return (o == null || o == DBNull.Value);
+                       return IsNull(Table.Columns[columnIndex]);
                }
 
                /// <summary>
@@ -960,8 +1235,7 @@ namespace System.Data {
                /// </summary>
                public bool IsNull (string columnName) 
                {
-                       object o = this[columnName];
-                       return (o == null || o == DBNull.Value);
+                       return IsNull(Table.Columns[columnName]);
                }
 
                /// <summary>
@@ -970,8 +1244,24 @@ namespace System.Data {
                /// </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>
@@ -983,22 +1273,29 @@ namespace System.Data {
                                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;
                                } 
                                
@@ -1010,7 +1307,12 @@ namespace System.Data {
                                // 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();
+                               }
                        }
                }
 
@@ -1027,9 +1329,13 @@ namespace System.Data {
                /// </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>
@@ -1051,7 +1357,6 @@ namespace System.Data {
                /// <summary>
                /// Sets the parent row of a DataRow with specified new parent DataRow.
                /// </summary>
-               [MonoTODO]
                public void SetParentRow (DataRow parentRow) 
                {
                        SetParentRow(parentRow, null);
@@ -1061,10 +1366,9 @@ namespace System.Data {
                /// 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)
@@ -1075,8 +1379,8 @@ namespace System.Data {
                        {
                                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++)
                                        {
@@ -1090,8 +1394,8 @@ namespace System.Data {
                        }
                        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++)
                                {
@@ -1107,83 +1411,55 @@ namespace System.Data {
                //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) {
@@ -1223,18 +1499,96 @@ namespace System.Data {
 
                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
        }