2004-05-14 Umadevi S <sumadevi@novell.com>
[mono.git] / mcs / class / System.Data / System.Data / DataRow.cs
index 55c41b9c97d3c9da82209f79bff1b10faad1e69d..fe0869907486b8a9370bb3edf6d71b8e206099e8 100644 (file)
@@ -16,6 +16,7 @@
 using System;
 using System.Collections;
 using System.Globalization;
+using System.Xml;
 
 namespace System.Data {
        /// <summary>
@@ -40,6 +41,11 @@ namespace System.Data {
                private string _nullConstraintMessage;
                private bool editing = false;
                private bool _hasParentCollection;
+               private bool _inChangingEvent;
+               private int _rowId;
+               internal bool _inExpressionEvaluation = false;
+
+               private XmlDataDocument.XmlDataElement mappedElement;
 
                #endregion
 
@@ -52,13 +58,19 @@ namespace System.Data {
                protected internal DataRow (DataRowBuilder builder)
                {
                        _table = builder.Table;
+                       // Get the row id from the builder.
+                       _rowId = builder._rowId;
 
                        original = null; 
                        
                        proposed = new object[_table.Columns.Count];
+                       // Initialise the data coloumns of the row with the dafault values, if any 
                        for (int c = 0; c < _table.Columns.Count; c++) 
                        {
-                               proposed[c] = DBNull.Value;
+                               if(_table.Columns [c].DefaultValue == null)
+                                       proposed[c] = DBNull.Value;
+                               else
+                                       proposed [c] = _table.Columns[c].DefaultValue;
                        }
                        
                        columnErrors = new string[_table.Columns.Count];
@@ -67,13 +79,17 @@ namespace System.Data {
                        //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 (string col in aiColumns) {
+                               DataColumn dc = _table.Columns[col];
+                               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);
                }
 
                
@@ -85,7 +101,6 @@ namespace System.Data {
                /// Gets a value indicating whether there are errors in a row.
                /// </summary>
                public bool HasErrors {
-                       [MonoTODO]
                        get {
                                if (RowError != string.Empty)
                                        return true;
@@ -143,7 +158,7 @@ namespace System.Data {
                                
                                bool orginalEditing = editing;
                                if (!orginalEditing) BeginEdit ();
-                               object v = SetColumnValue (value, columnIndex);
+                               object v = SetColumnValue (value, column, columnIndex);
                                proposed[columnIndex] = v;
                                _table.ChangedDataColumn (this, column, v);
                                if (!orginalEditing) EndEdit ();
@@ -167,9 +182,9 @@ 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];
                        }
                }
@@ -183,37 +198,63 @@ 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.");
-                               // Non-existent version
-                               if (rowState == DataRowState.Detached && version == DataRowVersion.Current || !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 ();
+                               
+                               DataColumn col = _table.Columns[columnIndex];
+                               if (col.Expression != String.Empty) {
+                                       object o = col.CompiledExpression.Eval (this);
+                                       return Convert.ChangeType (o, col.DataType);
+                               }
+                               
+                               if (HasVersion(version))
+                               {
+                                       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 ();
+                                       }
                                }
+                               if (rowState == DataRowState.Detached && version == DataRowVersion.Default && proposed == 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.");
+                               
+                               throw new VersionNotFoundException (Locale.GetText ("There is no " + version.ToString () + " data to access."));
                        }
                }
+               
+               internal void SetOriginalValue (string columnName, object val)
+               {
+                       int columnIndex = _table.Columns.IndexOf (columnName);
+                       DataColumn column = _table.Columns[columnIndex];
+                       _table.ChangingDataColumn (this, column, val);
+                               
+                       if (original == null) original = new object [_table.Columns.Count];
+                       val = SetColumnValue (val, column, columnIndex);
+                       original[columnIndex] = val;
+                       rowState = DataRowState.Modified;
+               }
 
                /// <summary>
                /// Gets or sets all of the values for this row through an array.
                /// </summary>
-               [MonoTODO]
                public object[] ItemArray {
                        get { 
-                               // Accessing deleted rows\r
-                               if (rowState == DataRowState.Deleted)\r
-                                       throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");\r
+                               // row not in table
+                               if (rowState == DataRowState.Detached)
+                                       throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
+                               // Accessing deleted rows
+                               if (rowState == DataRowState.Deleted)
+                                       throw new DeletedRowInaccessibleException ("Deleted row information cannot be accessed through the row.");
                                
                                return current; 
                        }
@@ -233,7 +274,7 @@ namespace System.Data {
                                        else
                                                v = null;
 
-                                       newItems[i] = SetColumnValue (v, i);
+                                       newItems[i] = SetColumnValue (v, _table.Columns[i], i);
                                }
 
                                bool orginalEditing = editing;
@@ -243,132 +284,147 @@ namespace System.Data {
                        }
                }
 
-               private object SetColumnValue (object v, int index) \r
-               {               \r
-                       object newval = null;\r
-                       DataColumn col = _table.Columns[index];\r
-                       \r
-                       if (_hasParentCollection && col.ReadOnly && v != this[index])\r
-                               throw new ReadOnlyException ();\r
-\r
-                       if (v == null)\r
-                       {\r
-                               if(col.DefaultValue != DBNull.Value) \r
-                               {\r
-                                       newval = col.DefaultValue;\r
-                               }\r
-                               else if(col.AutoIncrement == true) \r
-                               {\r
-                                       newval = this [index];\r
-                               }\r
-                               else \r
-                               {\r
-                                       if (!col.AllowDBNull)\r
-                                       {\r
-                                               //Constraint violations during data load is raise in DataTable EndLoad\r
-                                               this._nullConstraintViolation = true;\r
-                                               _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";\r
-                                       }\r
-\r
-                                       newval = DBNull.Value;\r
-                               }\r
-                       }               \r
-                       else if (v == DBNull.Value) \r
-                       {\r
-                               \r
-                               if (!col.AllowDBNull)\r
-                               {\r
-                                       //Constraint violations during data load is raise in DataTable EndLoad\r
-                                       this._nullConstraintViolation = true;\r
-                                       _nullConstraintMessage = "Column '" + col.ColumnName + "' does not allow nulls.";\r
-                               }\r
-                               \r
-                               newval = DBNull.Value;\r
-                       }\r
-                       else \r
-                       {       \r
-                               Type vType = v.GetType(); // data type of value\r
-                               Type cType = col.DataType; // column data type\r
-                               if (cType != vType) \r
-                               {\r
-                                       TypeCode typeCode = Type.GetTypeCode(cType);\r
-                                       switch(typeCode) {\r
-                                               case TypeCode.Boolean :\r
-                                                       v = Convert.ToBoolean (v);\r
-                                                       break;\r
-                                               case TypeCode.Byte  :\r
-                                                       v = Convert.ToByte (v);\r
-                                                       break;\r
-                                               case TypeCode.Char  :\r
-                                                       v = Convert.ToChar (v);\r
-                                                       break;\r
-                                               case TypeCode.DateTime  :\r
-                                                       v = Convert.ToDateTime (v);\r
-                                                       break;\r
-                                               case TypeCode.Decimal  :\r
-                                                       v = Convert.ToDecimal (v);\r
-                                                       break;\r
-                                               case TypeCode.Double  :\r
-                                                       v = Convert.ToDouble (v);\r
-                                                       break;\r
-                                               case TypeCode.Int16  :\r
-                                                       v = Convert.ToInt16 (v);\r
-                                                       break;\r
-                                               case TypeCode.Int32  :\r
-                                                       v = Convert.ToInt32 (v);\r
-                                                       break;\r
-                                               case TypeCode.Int64  :\r
-                                                       v = Convert.ToInt64 (v);\r
-                                                       break;\r
-                                               case TypeCode.SByte  :\r
-                                                       v = Convert.ToSByte (v);\r
-                                                       break;\r
-                                               case TypeCode.Single  :\r
-                                                       v = Convert.ToSingle (v);\r
-                                                       break;\r
-                                               case TypeCode.String  :\r
-                                                       v = Convert.ToString (v);\r
-                                                       break;\r
-                                               case TypeCode.UInt16  :\r
-                                                       v = Convert.ToUInt16 (v);\r
-                                                       break;\r
-                                               case TypeCode.UInt32  :\r
-                                                       v = Convert.ToUInt32 (v);\r
-                                                       break;\r
-                                               case TypeCode.UInt64  :\r
-                                                       v = Convert.ToUInt64 (v);\r
-                                                       break;\r
-                                               default :\r
-                                                               \r
-                                               switch(cType.ToString()) {\r
-                                                       case "System.TimeSpan" :\r
-                                                               v = (System.TimeSpan) v;\r
-                                                               break;\r
-                                                       case "System.Type" :\r
-                                                               v = (System.Type) v;\r
-                                                               break;\r
-                                                       case "System.Object" :\r
-                                                               //v = (System.Object) v;\r
-                                                               break;\r
-                                                       default:\r
-                                                               if (!cType.IsArray)\r
-                                                                       throw new InvalidCastException("Type not supported.");\r
-                                                               break;\r
-                                               }\r
-                                                       break;\r
-                                       }\r
-                                       vType = v.GetType();\r
-                               }\r
-                               newval = v;\r
-                               if(col.AutoIncrement == true) {\r
-                                       long inc = Convert.ToInt64(v);\r
-                                       col.UpdateAutoIncrementValue (inc);\r
-                               }\r
-                       }\r
-                       col.DataHasBeenSet = true;\r
-                       return newval;\r
-               }\r
+               private object SetColumnValue (object v, DataColumn col, int index) 
+               {               
+                       object newval = null;
+                       
+                       if (_hasParentCollection && col.ReadOnly && v != this[index])
+                               throw new ReadOnlyException ();
 
+                       if (v == null)
+                       {
+                               if (col.DataType.ToString().Equals("System.Guid"))
+                                       throw new ArgumentException("Cannot set column to be null, Please use DBNull instead");
+
+                               if(col.DefaultValue != DBNull.Value) 
+                               {
+                                       newval = col.DefaultValue;
+                               }
+                               else if(col.AutoIncrement == true && CanAccess(index,DataRowVersion.Default)) 
+                               {
+                                       // AutoIncrement column is already filled,
+                                       // so it just need to return existing value.
+                                       newval = this [index];
+                               }
+                               else 
+                               {
+                                       if (!col.AllowDBNull)
+                                       {
+                                               //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.";
+                                       }
+
+                                       newval = DBNull.Value;
+                               }
+                       }               
+                       else if (v == DBNull.Value) 
+                       {
+                               
+                               if (!col.AllowDBNull)
+                               {
+                                       //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.";
+                               }
+                               
+                               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();
+                               }
+
+                               // The MaxLength property is ignored for non-text columns
+                               if ((Type.GetTypeCode(vType) == TypeCode.String) && (col.MaxLength != -1) && 
+                                       (this.Table.Columns[index].MaxLength < ((string)v).Length)) {
+                                       throw new ArgumentException("Cannot set column '" + col.ColumnName + "' to '" + v + "'. The value violates the MaxLength limit of this column.");
+                               }
+                               newval = v;
+                               if(col.AutoIncrement == true) {
+                                       long inc = Convert.ToInt64(v);
+                                       col.UpdateAutoIncrementValue (inc);
+                               }
+                       }
+                       col.DataHasBeenSet = true;
+                       return newval;
+               }
 
                /// <summary>
                /// Gets or sets the custom error description for a row.
@@ -398,6 +454,7 @@ namespace System.Data {
                //from a Datatable so I added this method. Delete if there is a better way.
                internal void DetachRow() {
                        proposed = null;
+                       _rowId = -1;
                        _hasParentCollection = false;
                        rowState = DataRowState.Detached;
                }
@@ -409,6 +466,11 @@ namespace System.Data {
                        get { return _table; }
                }
 
+               internal XmlDataDocument.XmlDataElement DataElement {
+                       get { return mappedElement; }
+                       set { mappedElement = value; }
+               }
+
                /// <summary>
                /// Gets and sets index of row. This is used from 
                /// XmlDataDocument.
@@ -417,6 +479,14 @@ namespace System.Data {
                        get { return xmlRowID; }
                        set { xmlRowID = value; }
                }
+               
+               /// <summary>
+               /// Gets and sets index of row.
+               // </summary>
+               internal int RowID {
+                       get { return _rowId; }
+                       set { _rowId = value; }
+               }
 
                #endregion
 
@@ -436,6 +506,7 @@ namespace System.Data {
                                break;
                        case DataRowState.Deleted:
                                _table.Rows.RemoveInternal (this);
+                               DetachRow();
                                break;
                        case DataRowState.Detached:
                                throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
@@ -449,27 +520,29 @@ namespace System.Data {
                /// <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);
                        }
-                       //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)
@@ -490,19 +563,25 @@ namespace System.Data {
                /// <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:
-                               throw new DeletedRowInaccessibleException ();
+                               break;          
                        default:
                                // check what to do with child rows
                                CheckChildRows(DataRowAction.Delete);
+                               _table.DeleteRowFromIndexes (this);
                                rowState = DataRowState.Deleted;
                                break;
                        }
@@ -513,7 +592,7 @@ namespace System.Data {
                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.
 
@@ -525,35 +604,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.
@@ -572,8 +642,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];
                                                        }
                                                }
                                        }
@@ -585,8 +655,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);
                                                        }
@@ -602,8 +672,8 @@ namespace System.Data {
                                                        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;
+                                                               for (int k = 0; k < fkc.Columns.Length; k++)
+                                                                       child[fkc.Columns[k]] = fkc.Columns[k].DefaultValue;
                                                        }
                                                }
                                        }
@@ -617,8 +687,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]);
                                                        }
                                                }
                                        }
@@ -630,40 +700,70 @@ namespace System.Data {
                /// <summary>
                /// Ends the edit occurring on the row.
                /// </summary>
-               [MonoTODO]
                public void EndEdit () 
                {
-                       if (rowState == DataRowState.Detached)\r
-                       {\r
-                               editing = false;\r
-                               return;\r
-                       }\r
-\r
-                       if (HasVersion (DataRowVersion.Proposed))\r
-                       {\r
-                               _table.ChangingDataRow(this, DataRowAction.Change);\r
-                               if (rowState == DataRowState.Unchanged)\r
-                                       rowState = DataRowState.Modified;\r
-                               \r
-                               //Calling next method validates UniqueConstraints\r
-                               //and ForeignKeys.\r
-                               try\r
-                               {\r
-                                       if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)\r
-                                               _table.Rows.ValidateDataRowInternal(this);\r
-                               }\r
-                               catch (Exception e)\r
-                               {\r
-                                       editing = false;\r
-                                       proposed = null;\r
-                                       throw e;\r
-                               }\r
-                               // check all child rows.\r
-                               CheckChildRows(DataRowAction.Change);\r
-                               current = proposed;\r
-                               proposed = null;\r
-                               editing = false;\r
-                               _table.ChangedDataRow(this, DataRowAction.Change);\r
+                       if (_inChangingEvent)
+                               throw new InRowChangingEventException("Cannot call EndEdit inside an OnRowChanging event.");
+                       if (rowState == DataRowState.Detached)
+                       {
+                               editing = false;
+                               return;
+                       }
+                       
+                       CheckReadOnlyStatus();
+                       if (HasVersion (DataRowVersion.Proposed))
+                       {
+                               _inChangingEvent = true;
+                               try
+                               {
+                                       _table.ChangingDataRow(this, DataRowAction.Change);
+                               }
+                               finally
+                               {
+                                       _inChangingEvent = false;
+                               }
+                               if (rowState == DataRowState.Unchanged)
+                                       rowState = DataRowState.Modified;
+                               
+                               //Calling next method validates UniqueConstraints
+                               //and ForeignKeys.
+                               try
+                               {
+                                       if ((_table.DataSet == null || _table.DataSet.EnforceConstraints) && !_table._duringDataLoad)
+                                               _table.Rows.ValidateDataRowInternal(this);
+                               }
+                               catch (Exception e)
+                               {
+                                       editing = false;
+                                       proposed = null;
+                                       throw e;
+                               }
+
+                               // 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.
+                               object[] backup = current;
+                               current = proposed;
+                               bool editing_backup = editing;
+                               editing = false;
+                               try {
+                                       // check all child rows.
+                                       CheckChildRows(DataRowAction.Change);
+                                       proposed = null;
+                               }
+                               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);
                        }
                }
 
@@ -693,19 +793,25 @@ namespace System.Data {
                        if (relation == null)
                                return new DataRow[0];
 
-                       if (this.Table == null)
-                               throw new RowNotInTableException();
+                       if (this.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
 
                        if (relation.DataSet != this.Table.DataSet)
                                throw new ArgumentException();
 
-                       // TODO: Caching for better preformance
+                       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;
@@ -714,7 +820,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;
@@ -725,7 +831,9 @@ namespace System.Data {
                                        if (allColumnsMatch) rows.Add(row);
                                }
                        }
-                       return rows.ToArray(typeof(DataRow)) as DataRow[];
+                       DataRow[] result = relation.ChildTable.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
                }
 
                /// <summary>
@@ -737,6 +845,53 @@ 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))
+                       {
+                               object[] vals = new object[parentColumns.Length];
+                               for (int i = 0; i < vals.Length; i++)
+                                       vals[i] = this[parentColumns[i], version];
+
+                               Index index = fkc.Index;
+                               if (index != null) {
+                                       // get the child rows from the index
+                                       Node[] childNodes = index.FindAllSimple (vals);
+                                       for (int i = 0; i < childNodes.Length; i++) {
+                                               rows.Add (childNodes[i].Row);
+                                       }
+                               }
+                               else { // if there is no index we search manualy.
+                                       foreach (DataRow row in fkc.Table.Rows) 
+                                       {
+                                               bool allColumnsMatch = false;
+                                               if (row.HasVersion(DataRowVersion.Default))
+                                               {
+                                                       allColumnsMatch = true;
+                                                       for (int columnCnt = 0; columnCnt < numColumn; ++columnCnt) 
+                                                       {
+                                                               if (!vals[columnCnt].Equals(
+                                                                       row[childColumns[columnCnt], DataRowVersion.Default])) 
+                                                               {
+                                                                       allColumnsMatch = false;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               if (allColumnsMatch) rows.Add(row);
+                                       }
+                               }
+                       }
+
+                       DataRow[] result = fkc.Table.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
+               }
+
                /// <summary>
                /// Gets the error description of the specified DataColumn.
                /// </summary>
@@ -847,8 +1002,8 @@ namespace System.Data {
                        if (relation == null)
                                return new DataRow[0];
 
-                       if (this.Table == null)
-                               throw new RowNotInTableException();
+                       if (this.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
 
                        if (relation.DataSet != this.Table.DataSet)
                                throw new ArgumentException();
@@ -859,26 +1014,42 @@ namespace System.Data {
                        int numColumn = parentColumns.Length;
                        if (HasVersion(version))
                        {
-                               foreach (DataRow row in relation.ParentTable.Rows) 
-                               {
-                                       bool allColumnsMatch = false;
-                                       if (row.HasVersion(DataRowVersion.Default))
+                               object[] vals = new object[childColumns.Length];
+                               for (int i = 0; i < vals.Length; i++)
+                                       vals[i] = this[childColumns[i], version];
+                               
+                               Index indx = relation.ParentTable.GetIndexByColumns (parentColumns);
+                               if (indx != null) { // get the child rows from the index
+                                       Node[] childNodes = indx.FindAllSimple (vals);
+                                       for (int i = 0; i < childNodes.Length; i++) {
+                                               rows.Add (childNodes[i].Row);
+                                       }
+                               }
+                               else { // no index so we have to search manualy.
+                                       foreach (DataRow row in relation.ParentTable.Rows) 
                                        {
-                                               allColumnsMatch = true;
-                                               for (int columnCnt = 0; columnCnt < numColumn; columnCnt++) 
+                                               bool allColumnsMatch = false;
+                                               if (row.HasVersion(DataRowVersion.Default))
                                                {
-                                                       if (!this[childColumns[columnCnt], version].Equals(
-                                                               row[parentColumns[columnCnt], DataRowVersion.Default])
+                                                       allColumnsMatch = true;
+                                                       for (int columnCnt = 0; columnCnt < numColumn; columnCnt++
                                                        {
-                                                               allColumnsMatch = false;
-                                                               break;
+                                                               if (!this[childColumns[columnCnt], version].Equals(
+                                                                       row[parentColumns[columnCnt], DataRowVersion.Default])) 
+                                                               {
+                                                                       allColumnsMatch = false;
+                                                                       break;
+                                                               }
                                                        }
                                                }
+                                               if (allColumnsMatch) rows.Add(row);
                                        }
-                                       if (allColumnsMatch) rows.Add(row);
                                }
                        }
-                       return rows.ToArray(typeof(DataRow)) as DataRow[];
+
+                       DataRow[] result = relation.ParentTable.NewRowArray(rows.Count);
+                       rows.CopyTo(result, 0);
+                       return result;
                }
 
                /// <summary>
@@ -898,17 +1069,17 @@ namespace System.Data {
                        switch (version)
                        {
                                case DataRowVersion.Default:
-                                       if (rowState == DataRowState.Deleted)
+                                       if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
                                                return false;
                                        if (rowState == DataRowState.Detached)
                                                return proposed != null;
                                        return true;
                                case DataRowVersion.Proposed:
-                                       if (rowState == DataRowState.Deleted)
+                                       if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
                                                return false;
                                        return (proposed != null);
                                case DataRowVersion.Current:
-                                       if (rowState == DataRowState.Deleted || rowState == DataRowState.Detached)
+                                       if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
                                                return false;
                                        return (current != null);
                                case DataRowVersion.Original:
@@ -925,7 +1096,7 @@ namespace System.Data {
                public bool IsNull (DataColumn column) 
                {
                        object o = this[column];
-                       return (o == null || o == DBNull.Value);
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -935,7 +1106,7 @@ namespace System.Data {
                public bool IsNull (int columnIndex) 
                {
                        object o = this[columnIndex];
-                       return (o == null || o == DBNull.Value);
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -944,7 +1115,7 @@ namespace System.Data {
                public bool IsNull (string columnName) 
                {
                        object o = this[columnName];
-                       return (o == null || o == DBNull.Value);
+                       return (o == DBNull.Value);
                }
 
                /// <summary>
@@ -954,7 +1125,24 @@ namespace System.Data {
                public bool IsNull (DataColumn column, DataRowVersion version) 
                {
                        object o = this[column, version];
-                       return (o == null || o == DBNull.Value);
+                       return (o == DBNull.Value);
+               }
+
+               /// <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>
@@ -962,6 +1150,8 @@ namespace System.Data {
                /// </summary>
                public void RejectChanges () 
                {
+                       if (RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
                        // If original is null, then nothing has happened since AcceptChanges
                        // was last called.  We have no "original" to go back to.
                        if (original != null)
@@ -973,13 +1163,18 @@ namespace System.Data {
                                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;
                                } 
                                
@@ -991,7 +1186,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();
+                               }
                        }
                }
 
@@ -1032,7 +1232,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);
@@ -1042,11 +1241,10 @@ 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)
-                               throw new RowNotInTableException();
+                       if (_table == null || parentRow.Table == null || RowState == DataRowState.Detached)
+                               throw new RowNotInTableException("This row has been removed from a table and does not have any data.  BeginEdit() will allow creation of new data in this row.");
 
                        if (parentRow != null && _table.DataSet != parentRow.Table.DataSet)
                                throw new ArgumentException();
@@ -1099,26 +1297,27 @@ namespace System.Data {
                        for(int i = 0; i < columns.Count; i++){
 
                                string columnName = columns[i].ColumnName;
-                               int index = row.Table.Columns.IndexOf(columnName);
+                               DataColumn column = row.Table.Columns[columnName];
                                //if a column with the same name exists in both rows copy the values
-                               if(index != -1) {
+                               if(column != null) {
+                                       int index = column.Ordinal;
                                        if (HasVersion(DataRowVersion.Original))
                                        {
                                                if (row.original == null)
                                                        row.original = new object[row.Table.Columns.Count];
-                                               row.original[index] = row.SetColumnValue(original[i], index);
+                                               row.original[index] = row.SetColumnValue(original[i], column, index);
                                        }
                                        if (HasVersion(DataRowVersion.Current))
                                        {
                                                if (row.current == null)
                                                        row.current = new object[row.Table.Columns.Count];
-                                               row.current[index] = row.SetColumnValue(current[i], index);
+                                               row.current[index] = row.SetColumnValue(current[i], column, index);
                                        }
                                        if (HasVersion(DataRowVersion.Proposed))
                                        {
                                                if (row.proposed == null)
                                                        row.proposed = new object[row.Table.Columns.Count];
-                                               row.proposed[index] = row.SetColumnValue(proposed[i], index);
+                                               row.proposed[index] = row.SetColumnValue(proposed[i], column, index);
                                        }
                                        
                                        //Saving the current value as the column value
@@ -1133,7 +1332,7 @@ namespace System.Data {
                }
 
                
-               public void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
+               private void CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs args)
                {
                        // if a column is added we hava to add an additional value the 
                        // the priginal, current and propoed arrays.
@@ -1142,31 +1341,76 @@ namespace System.Data {
                        if (args.Action == System.ComponentModel.CollectionChangeAction.Add)
                        {
                                object[] tmp;
+                               int index = this.Table.Columns.Count - 1;
                                if (current != null)
                                {
-                                       tmp = new object[current.Length + 1];
+                                       tmp = new object [index + 1];
                                        Array.Copy (current, tmp, current.Length);
-                                       tmp[tmp.Length - 1] = DBNull.Value;
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
                                        current = tmp;
                                }
                                if (proposed != null)
                                {
-                                       tmp = new object[proposed.Length + 1];
+                                       tmp = new object [index + 1];
                                        Array.Copy (proposed, tmp, proposed.Length);
-                                       tmp[tmp.Length - 1] = DBNull.Value;
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
                                        proposed = tmp;
                                }
                                if(original != null)
                                {
-                                       tmp = new object[original.Length + 1];
+                                       tmp = new object [index + 1];
                                        Array.Copy (original, tmp, original.Length);
-                                       tmp[tmp.Length - 1] = DBNull.Value;
+                                       tmp[tmp.Length - 1] = SetColumnValue(null, this.Table.Columns[index], index);
                                        original = tmp;
                                }
 
                        }
                }
 
+               internal void onColumnRemoved(int columnIndex)
+               {
+                       // when column removed we have to compress row values in the way 
+                       // they will correspond to new column ordinals
+
+                       object[] tmp;
+                       if (current != null)
+                       {
+                               tmp = new object[current.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (current, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < current.Length - 1)
+                                       Array.Copy(current, columnIndex + 1, tmp, columnIndex, current.Length - 1 - columnIndex);
+
+                               current = tmp;
+                       }
+                       if (proposed != null)
+                       {
+                               tmp = new object[proposed.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (proposed, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < proposed.Length - 1)
+                                       Array.Copy(proposed, columnIndex + 1, tmp, columnIndex, proposed.Length - 1 - columnIndex);
+
+                               proposed = tmp;
+                       }
+                       if (original != null)
+                       {
+                               tmp = new object[original.Length - 1];
+                               // copy values before removed column
+                               if (columnIndex > 0)
+                                       Array.Copy (original, 0, tmp, 0, columnIndex);
+                               // copy values after removed column
+                               if(columnIndex < original.Length - 1)
+                                       Array.Copy(original, columnIndex + 1, tmp, columnIndex, original.Length - 1 - columnIndex);
+
+                               original = tmp;
+                       }
+               }
+
                internal bool IsRowChanged(DataRowState rowState) {
                        if((RowState & rowState) != 0)
                                return true;
@@ -1202,19 +1446,73 @@ namespace System.Data {
                        }
                }
 
-               internal void CheckNullConstraints()\r
-               {\r
-                       if (_nullConstraintViolation)\r
-                       {\r
-                               for (int i = 0; i < proposed.Length; i++)\r
-                               {\r
-                                       if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)\r
-                                               throw new NoNullAllowedException(_nullConstraintMessage);\r
-                               }\r
-                               _nullConstraintViolation = false;\r
-                       }\r
+               // checks existance of value in version pecified
+               // note : this one relies on the same algorithm as this[int,DataRowVersion]
+               private bool CanAccess(int columnIndex, DataRowVersion version) 
+               {
+                       if (columnIndex < 0 || columnIndex > _table.Columns.Count)
+                               return false;
+                       // Accessing deleted rows
+                       if (rowState == DataRowState.Deleted && version != DataRowVersion.Original)
+                               return false;
+
+                       if (HasVersion(version))
+                       {
+                               object[] versionArr;
+                               switch (version) 
+                               {
+                                       case DataRowVersion.Default:
+                                               if (editing || rowState == DataRowState.Detached) {
+                                                       versionArr = proposed;
+                                               }
+                                               else {
+                                                       versionArr = current;
+                                               }
+                                               break;
+                                       case DataRowVersion.Proposed:
+                                               versionArr = proposed;
+                                               break;
+                                       case DataRowVersion.Current:
+                                               versionArr = current;
+                                               break;
+                                       case DataRowVersion.Original:
+                                               versionArr = original;
+                                               break;
+                                       default:
+                                               return false;
+                               }
+
+                               return (versionArr != null && columnIndex < versionArr.Length);
+                       }
+                       
+                       return false;
                }
 
+               internal void CheckNullConstraints()
+               {
+                       if (_nullConstraintViolation) {
+                               if (proposed != null) {
+                                       for (int i = 0; i < proposed.Length; i++) {
+                                               if (this[i] == DBNull.Value && !_table.Columns[i].AllowDBNull)
+                                                       throw new NoNullAllowedException(_nullConstraintMessage);
+                                       }
+                               }
+                               _nullConstraintViolation = false;
+                       }
+               }
+               
+               internal void CheckReadOnlyStatus()
+                {
+                       if (proposed == null)
+                               return;
+
+                       for (int i = 0; i < proposed.Length; i++) {
+                               if (this[i] != proposed[i] && _table.Columns[i].ReadOnly)
+                                       throw new ReadOnlyException();
+                        }
+                           
+                }
+       
                #endregion // Methods
        }