Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / DataRow.cs / 1305376 / DataRow.cs

    //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
namespace System.Data {
    using System; 
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization; 
    using System.Xml;
 
 
    /// 
    /// Represents a row of data in a . 
    /// 
    public class DataRow {
        private readonly DataTable _table;
        private readonly DataColumnCollection _columns; 

        internal int  oldRecord = -1; 
        internal int  newRecord = -1; 
        internal int  tempRecord;
        internal long  _rowID = -1; 

        internal DataRowAction _action;

        internal bool  inChangingEvent; 
        internal bool  inDeletingEvent;
        internal bool  inCascade; 
 
        private DataColumn _lastChangedColumn; // last successfully changed column
        private int _countColumnChange;        // number of columns changed during edit mode 

        private DataError  error;
        private object    _element;
 
        private int _rbTreeNodeId; // if row is not detached, Id used for computing index in rows collection
 
        private static int _objectTypeCount; // Bid counter 
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        /// 
        ///    
        ///       Initializes a new instance of the DataRow.
        ///     
        ///    
        ///       Constructs a row from the builder. Only for internal usage.. 
        ///     
        /// 
        protected internal DataRow (DataRowBuilder builder) { 
            tempRecord = builder._record;
            _table = builder._table;
            _columns = _table.Columns;
        } 

        internal XmlBoundElement  Element { 
            get { 
                return (XmlBoundElement) _element;
            } 
            set {
                _element = value;
            }
        } 

        internal DataColumn LastChangedColumn { 
            get { // last successfully changed column or if multiple columns changed: null 
                if (_countColumnChange != 1) {
                    return null; 
                }
                return _lastChangedColumn;
            }
            set { 
                _countColumnChange++;
                _lastChangedColumn = value; 
            } 
        }
 
        internal bool HasPropertyChanged {
            get { return (0 < _countColumnChange); }
        }
 
        internal int RBTreeNodeId {
            get { 
                return _rbTreeNodeId; 
            }
            set { 
                Bid.Trace(" %d#, value=%d\n", ObjectID, value);
                _rbTreeNodeId = value;
            }
        } 

        ///  
        ///    Gets or sets the custom error description for a row. 
        /// 
        public string RowError { 
            get {
                return(error == null ? String.Empty :error.Text);
            }
            set { 
                Bid.Trace(" %d#, value='%ls'\n", ObjectID, value);
                if (error == null) { 
                    if (!Common.ADP.IsEmpty(value)) { 
                        error = new DataError(value);
                    } 
                    RowErrorChanged();
                }
                else if(error.Text != value) {
                    error.Text = value; 
                    RowErrorChanged();
                } 
            } 
        }
 
        private void RowErrorChanged() {
            // We don't know wich record was used by view index. try to use both.
            if (oldRecord != -1)
                _table.RecordChanged(oldRecord); 
            if (newRecord != -1)
                _table.RecordChanged(newRecord); 
        } 

        internal long rowID { 
            get {
                return _rowID;
            }
            set { 
                ResetLastChangedColumn();
                _rowID = value; 
            } 
        }
 
        /// 
        ///    Gets the current state of the row in regards to its relationship to the table.
        /// 
        public DataRowState RowState { 
            get {
                /* 
                if (oldRecord == -1 && newRecord == -1) 
                    state = DataRowState.Detached; // 2
                else if (oldRecord == newRecord) 
                    state = DataRowState.Unchanged; // 2
                else if (oldRecord == -1)
                    state = DataRowState.Added; // 4
                else if (newRecord == -1) 
                    state = DataRowState.Deleted; // 4
                else 
                    state = DataRowState.Modified; // 4 
                */
                if (oldRecord == newRecord) { 
                    if (oldRecord == -1) {
                        return DataRowState.Detached; // 2
                    }
                    if (0 < _columns.ColumnsImplementingIChangeTrackingCount) { 
                        foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
                            object value = this[dc]; 
                            if ((DBNull.Value != value) && ((IChangeTracking)value).IsChanged) { 
                                return DataRowState.Modified; // 3 + _columns.columnsImplementingIChangeTracking.Count
                            } 
                        }
                    }
                    return DataRowState.Unchanged; // 3
                } 
                else if (oldRecord == -1) {
                    return DataRowState.Added; // 2 
                } 
                else if (newRecord == -1) {
                    return DataRowState.Deleted; // 3 
                }
                return DataRowState.Modified; // 3

            } 
        }
 
        ///  
        /// Gets the 
        /// for which this row has a schema. 
        /// 
        public DataTable Table {
            get {
                return _table; 
            }
        } 
 
        /// 
        ///    Gets or sets the data stored in the column specified by index. 
        /// 
        public object this[int columnIndex] {
            get {
                DataColumn column = _columns[columnIndex]; 
                int record = GetDefaultRecord();
                _table.recordManager.VerifyRecord(record, this); 
                VerifyValueFromStorage(column, DataRowVersion.Default, column[record]); 
                return column[record];
            } 
            set {
                DataColumn column = _columns[columnIndex];
                this[column] = value;
            } 
        }
 
        internal void CheckForLoops(DataRelation rel){ 
            // don't check for loops in the diffgram
            // because there may be some holes in the rowCollection 
            // and index creation may fail. The check will be done
            // after all the loading is done _and_ we are sure there
            // are no holes in the collection.
            if (_table.fInLoadDiffgram || (_table.DataSet != null && _table.DataSet.fInLoadDiffgram)) 
              return;
            int count = _table.Rows.Count, i = 0; 
            // need to optimize this for count > 100 
            DataRow parent = this.GetParentRow(rel);
            while (parent != null) { 
                if ((parent == this) || (i>count))
                    throw ExceptionBuilder.NestedCircular(_table.TableName);
                i++;
                parent = parent.GetParentRow(rel); 
            }
        } 
 
        internal int GetNestedParentCount() {
            int count = 0; 
            DataRelation[] nestedParentRelations = _table.NestedParentRelations;
            foreach(DataRelation rel in nestedParentRelations) {
                if (rel == null) // don't like this but done for backward code compatability
                    continue; 
                if (rel.ParentTable == _table) // self-nested table
                    this.CheckForLoops(rel); 
                DataRow row = this.GetParentRow(rel); 
                if (row != null) {
                    count++; 
                }
            }
            return count ;
            // Rule 1: At all times, only ONE FK  "(in a row) can be non-Null 
            // we wont allow a row to have multiple parents, as we cant handle it , also in diffgram
        } 
 
        /// 
        ///    Gets or sets the data stored in the column specified by 
        ///       name.
        /// 
        public object this[string columnName] {
            get { 
                DataColumn column = GetDataColumn(columnName);
                int record = GetDefaultRecord(); 
                _table.recordManager.VerifyRecord(record, this); 
                VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
                return column[record]; 
            }
            set {
                DataColumn column = GetDataColumn(columnName);
                this[column] = value; 
            }
        } 
 
        /// 
        ///    Gets or sets 
        ///       the data stored in the specified .
        /// 
        public object this[DataColumn column] {
            get { 
                CheckColumn(column);
                int record = GetDefaultRecord(); 
                _table.recordManager.VerifyRecord(record, this); 
                VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
                return column[record]; 
            }
            set {
                CheckColumn(column);
                if (inChangingEvent) { 
                    throw ExceptionBuilder.EditInRowChanging();
                } 
                if ((-1 != rowID) && column.ReadOnly) { 
                    throw ExceptionBuilder.ReadOnly(column.ColumnName);
                } 

                // allow users to tailor the proposed value, or throw an exception.
                // note we intentionally do not try/catch this event.
                // note: we also allow user to do anything at this point 
                // infinite loops are possible if user calls Item or ItemArray during the event
                DataColumnChangeEventArgs e = null; 
                if (_table.NeedColumnChangeEvents) { 
                    e = new DataColumnChangeEventArgs(this, column, value);
                    _table.OnColumnChanging(e); 
                }

                if (column.Table != _table) {
                    // user removed column from table during OnColumnChanging event 
                    throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
                } 
                if ((-1 != rowID) && column.ReadOnly) { 
                    // user adds row to table during OnColumnChanging event
                    throw ExceptionBuilder.ReadOnly(column.ColumnName); 
                }

                object proposed = ((null != e) ? e.ProposedValue : value);
                if (null == proposed) { 
                    if (column.IsValueType) { // WebData 105963
                        throw ExceptionBuilder.CannotSetToNull(column); 
                    } 
                    proposed = DBNull.Value;
                } 

                bool immediate = BeginEditInternal();
                try {
                    int record = GetProposedRecordNo(); 
                    _table.recordManager.VerifyRecord(record, this);
                    column[record] = proposed; 
                } 
                catch (Exception e1){
                    // 
                    if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) {
                        if (immediate) {
                            Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
                            Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?"); 
                            CancelEdit(); // WebData 107154
                        } 
                    } 
                    throw;
                } 
                LastChangedColumn = column;

                // note: we intentionally do not try/catch this event.
                // infinite loops are possible if user calls Item or ItemArray during the event 
                if (null != e) {
                    _table.OnColumnChanged(e); // user may call CancelEdit or EndEdit 
                } 

                if (immediate) { 
                    Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
                    EndEdit();
                }
            } 
        }
 
        ///  
        ///    Gets the data stored
        ///       in the column, specified by index and version of the data to retrieve. 
        /// 
        public object this[int columnIndex, DataRowVersion version] {
            get {
                DataColumn column = _columns[columnIndex]; 
                int record = GetRecordFromVersion(version);
                _table.recordManager.VerifyRecord(record, this); 
                VerifyValueFromStorage(column, version, column[record]); 
                return column[record];
            } 
        }

        /// 
        ///     Gets the specified version of data stored in 
        ///       the named column.
        ///  
        public object this[string columnName, DataRowVersion version] { 
            get {
                DataColumn column = GetDataColumn(columnName); 
                int record = GetRecordFromVersion(version);
                _table.recordManager.VerifyRecord(record, this);
                VerifyValueFromStorage(column, version, column[record]);
                return column[record]; 
            }
        } 
 
        /// 
        /// Gets the specified version of data stored in the specified . 
        /// 
        public object this[DataColumn column, DataRowVersion version] {
            get {
                CheckColumn(column); 
                int record = GetRecordFromVersion(version);
                _table.recordManager.VerifyRecord(record, this); 
                VerifyValueFromStorage(column, version, column[record]); 
                return column[record];
            } 
        }

        /// 
        ///    Gets 
        ///       or sets all of the values for this row through an array.
        ///  
        public object[] ItemArray { 
            get {
                int record = GetDefaultRecord(); 
                _table.recordManager.VerifyRecord(record, this);
                object[] values = new object[_columns.Count];
                for (int i = 0; i < values.Length; i++) {
                    DataColumn column = _columns[i]; 
                    VerifyValueFromStorage(column, DataRowVersion.Default, column[record]);
                    values[i] = column[record]; 
                } 
                return values;
            } 
            set {
                if (null == value) { // WebData 104372
                    throw ExceptionBuilder.ArgumentNull("ItemArray");
                } 
                if (_columns.Count < value.Length) {
                    throw ExceptionBuilder.ValueArrayLength(); 
                } 
                DataColumnChangeEventArgs e = null;
                if (_table.NeedColumnChangeEvents) { 
                    e = new DataColumnChangeEventArgs(this);
                }
                bool immediate = BeginEditInternal();
 
                for (int i = 0; i < value.Length; ++i) {
                    // Empty means don't change the row. 
                    if (null != value[i]) { 
                        // may throw exception if user removes column from table during event
                        DataColumn column = _columns[i]; 

                        if ((-1 != rowID) && column.ReadOnly) {
                            throw ExceptionBuilder.ReadOnly(column.ColumnName);
                        } 

                        // allow users to tailor the proposed value, or throw an exception. 
                        // note: we intentionally do not try/catch this event. 
                        // note: we also allow user to do anything at this point
                        // infinite loops are possible if user calls Item or ItemArray during the event 
                        if (null != e) {
                            e.InitializeColumnChangeEvent(column, value[i]);
                            _table.OnColumnChanging(e);
                        } 

                        if (column.Table != _table) { 
                            // user removed column from table during OnColumnChanging event 
                            throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
                        } 
                        if ((-1 != rowID) && column.ReadOnly) {
                            // user adds row to table during OnColumnChanging event
                            throw ExceptionBuilder.ReadOnly(column.ColumnName);
                        } 
                        if (tempRecord == -1) {
                            // user affected CancelEdit or EndEdit during OnColumnChanging event of the last value 
                            BeginEditInternal(); 
                        }
 
                        object proposed = (null != e) ? e.ProposedValue : value[i];
                        if (null == proposed) {
                            if (column.IsValueType) { // WebData 105963
                                throw ExceptionBuilder.CannotSetToNull(column); 
                            }
                            proposed = DBNull.Value; 
                        } 

                        try { 
                            // must get proposed record after each event because user may have
                            // called EndEdit(), AcceptChanges(), BeginEdit() during the event
                            int record = GetProposedRecordNo();
                            _table.recordManager.VerifyRecord(record, this); 
                            column[record] = proposed;
                        } 
                        catch (Exception e1) { 
                            //
                            if (Common.ADP.IsCatchableOrSecurityExceptionType(e1)) { 
                                if (immediate) {
                                    Debug.Assert(!inChangingEvent, "how are we in a changing event to cancel?");
                                    Debug.Assert(-1 != tempRecord, "how no propsed record to cancel?");
                                    CancelEdit(); // WebData 107154 
                                }
                            } 
                            throw; 
                        }
                        LastChangedColumn = column; 

                        // note: we intentionally do not try/catch this event.
                        // infinite loops are possible if user calls Item or ItemArray during the event
                        if (null != e) { 
                            _table.OnColumnChanged(e);  // user may call CancelEdit or EndEdit
                        } 
                    } 
                }
 
                // proposed breaking change: if (immediate){ EndEdit(); } because table currently always fires RowChangedEvent
                Debug.Assert(!inChangingEvent, "how are we in a changing event to end?");
                EndEdit();
            } 
        }
 
        ///  
        ///    Commits all the changes made to this row
        ///       since the last time  was called. 
        /// 
        public void AcceptChanges() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); 
            try {
                EndEdit(); 
 
                if (this.RowState != DataRowState.Detached && this.RowState != DataRowState.Deleted) {
                    if (_columns.ColumnsImplementingIChangeTrackingCount > 0) { 
                        foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
                            object value = this[dc];
                            if (DBNull.Value != value) {
                                IChangeTracking tracking = (IChangeTracking)value; 
                                if (tracking.IsChanged) {
                                    tracking.AcceptChanges(); 
                                } 
                            }
                        } 
                    }
                }
                _table.CommitRow(this);
            } 
            finally {
                Bid.ScopeLeave(ref hscp); 
            } 
        }
 
        /// 
        /// Begins an edit operation on a object.
        /// 
        [ 
        EditorBrowsableAttribute(EditorBrowsableState.Advanced),
        ] 
        public void BeginEdit() { 
            BeginEditInternal();
        } 

        private bool BeginEditInternal() {
            if (inChangingEvent) {
                throw ExceptionBuilder.BeginEditInRowChanging(); 
            }
            if (tempRecord != -1) { 
                if (tempRecord < _table.recordManager.LastFreeRecord) { 
                    return false; // we will not call EndEdit
                } 
                else {
                    // partial fix for detached row after Table.Clear scenario
                    // in debug, it will have asserted earlier, but with this
                    // it will go get a new record for editing 
                    tempRecord = -1;
                } 
                // shifted VerifyRecord to first make the correction, then verify 
                _table.recordManager.VerifyRecord(tempRecord, this);
            } 

            if (oldRecord != -1 && newRecord == -1) {
                throw ExceptionBuilder.DeletedRowInaccessible();
            } 

            // 
 
            ResetLastChangedColumn(); // shouldn't have to do this
 
            tempRecord = _table.NewRecord(newRecord);
            Debug.Assert(-1 != tempRecord, "missing temp record");
            Debug.Assert(0 == _countColumnChange, "unexpected column change count");
            Debug.Assert(null == _lastChangedColumn, "unexpected last column change"); 
            return true;
        } 
 
        /// 
        ///    Cancels the current edit on the row. 
        /// 
        [
        EditorBrowsableAttribute(EditorBrowsableState.Advanced),
        ] 
        public void CancelEdit() {
            if (inChangingEvent) { 
                throw ExceptionBuilder.CancelEditInRowChanging(); 
            }
 
            _table.FreeRecord(ref tempRecord);
            Debug.Assert(-1 == tempRecord, "unexpected temp record");
            ResetLastChangedColumn();
        } 

        private void CheckColumn(DataColumn column) { 
            if (column == null) { 
                throw ExceptionBuilder.ArgumentNull("column");
            } 

            if (column.Table != _table) {
                throw ExceptionBuilder.ColumnNotInTheTable(column.ColumnName, _table.TableName);
            } 
        }
 
        ///  
        /// Throws a RowNotInTableException if row isn't in table.
        ///  
        internal void CheckInTable() {
            if (rowID == -1) {
                throw ExceptionBuilder.RowNotInTheTable();
            } 
        }
 
        ///  
        ///    Deletes the row.
        ///  
        public void Delete() {
            if (inDeletingEvent) {
                throw ExceptionBuilder.DeleteInRowDeleting();
            } 

            if (newRecord == -1) 
                return; 

            _table.DeleteRow(this); 
        }

        /// 
        ///    Ends the edit occurring on the row. 
        /// 
        [ 
        EditorBrowsableAttribute(EditorBrowsableState.Advanced), 
        ]
        public void EndEdit() { 
            if (inChangingEvent) {
                throw ExceptionBuilder.EndEditInRowChanging();
            }
 
            if (newRecord == -1) {
                return; // this is meaningless, detatched row case 
            } 

            if (tempRecord != -1) { 
                try {
                    // suppressing the ensure property changed because it's possible that no values have been modified
                    _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true);
                } 
                finally {
                    // a constraint violation may be thrown during SetNewRecord 
                    ResetLastChangedColumn(); 
                }
            } 
        }

        /// 
        ///    Sets the error description for a column specified by index. 
        /// 
        public void SetColumnError(int columnIndex, string error) { 
            DataColumn column = _columns[columnIndex]; 
            if (column == null)
                throw ExceptionBuilder.ColumnOutOfRange(columnIndex); 

            SetColumnError(column, error);
        }
 
        /// 
        ///    Sets 
        ///       the error description for a column specified by name. 
        /// 
        public void SetColumnError(string columnName, string error) { 
            DataColumn column = GetDataColumn(columnName);
            SetColumnError(column, error);
        }
 
        /// 
        /// Sets the error description for a column specified as a . 
        ///  
        public void SetColumnError(DataColumn column, string error) {
            CheckColumn(column); 

            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " %d#, column=%d, error='%ls'\n", ObjectID, column.ObjectID, error);
            try { 
                if (this.error == null)  this.error = new DataError();
                if(GetColumnError(column) != error) { 
                    this.error.SetColumnError(column, error); 
                    RowErrorChanged();
                } 
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            } 
        }
 
        ///  
        ///    Gets the error description for the column specified
        ///       by index. 
        /// 
        public string GetColumnError(int columnIndex) {
            DataColumn column = _columns[columnIndex];
            return GetColumnError(column); 
        }
 
        ///  
        ///    Gets the error description for a column, specified by name.
        ///  
        public string GetColumnError(string columnName) {
            DataColumn column = GetDataColumn(columnName);
            return GetColumnError(column);
        } 

        ///  
        ///    Gets the error description of 
        ///       the specified .
        ///  
        public string GetColumnError(DataColumn column) {
            CheckColumn(column);
            if (error == null)  error = new DataError();
            return error.GetColumnError(column); 
        }
 
        ///  
        /// Clears the errors for the row, including the 
        /// and errors set with  
        /// 
        public void ClearErrors() {
            if (error != null) {
                error.Clear(); 
                RowErrorChanged();
            } 
        } 

        internal void ClearError(DataColumn column) { 
            if (error != null) {
                error.Clear(column);
                RowErrorChanged();
            } 
        }
 
        ///  
        ///    Gets a value indicating whether there are errors in a columns collection.
        ///  
        public bool HasErrors {
            get {
                return(error == null ? false : error.HasErrors);
            } 
        }
 
        ///  
        ///    Gets an array of columns that have errors.
        ///  
        public DataColumn[] GetColumnsInError() {
            if (error == null)
                return DataTable.zeroColumns;
            else 
                return error.GetColumnsInError();
        } 
 

        public DataRow[] GetChildRows(string relationName) { 
            return GetChildRows(_table.ChildRelations[relationName], DataRowVersion.Default);
        }

        public DataRow[] GetChildRows(string relationName, DataRowVersion version) { 
            return GetChildRows(_table.ChildRelations[relationName], version);
        } 
 
        /// 
        /// Gets the child rows of this  using the 
        ///    specified 
        ///    .
        /// 
        public DataRow[] GetChildRows(DataRelation relation) { 
            return GetChildRows(relation, DataRowVersion.Default);
        } 
 
        /// 
        /// Gets the child rows of this  using the specified  and the specified  
        /// 
        public DataRow[] GetChildRows(DataRelation relation, DataRowVersion version) {
            if (relation == null)
                return _table.NewRowArray(0); 

            //if (-1 == rowID) 
            //    throw ExceptionBuilder.RowNotInTheTable(); 

            if (relation.DataSet != _table.DataSet) 
                throw ExceptionBuilder.RowNotInTheDataSet();
            if (relation.ParentKey.Table != _table)
                throw ExceptionBuilder.RelationForeignTable(relation.ParentTable.TableName, _table.TableName);
            return DataRelation.GetChildRows(relation.ParentKey, relation.ChildKey, this, version); 
        }
 
        internal DataColumn GetDataColumn(string columnName) { 
            DataColumn column = _columns[columnName];
            if (null != column) { 
                return column;
            }
            throw ExceptionBuilder.ColumnNotInTheTable(columnName, _table.TableName);
        } 

        public DataRow GetParentRow(string relationName) { 
            return GetParentRow(_table.ParentRelations[relationName], DataRowVersion.Default); 
        }
 
        public DataRow GetParentRow(string relationName, DataRowVersion version) {
            return GetParentRow(_table.ParentRelations[relationName], version);
        }
 
        /// 
        /// Gets the parent row of this  using the specified  . 
        ///  
        public DataRow GetParentRow(DataRelation relation) {
            return GetParentRow(relation, DataRowVersion.Default); 
        }

        /// 
        /// Gets the parent row of this  
        /// using the specified  and .
        ///  
        public DataRow GetParentRow(DataRelation relation, DataRowVersion version) { 
            if (relation == null)
                return null; 

            //if (-1 == rowID)
            //    throw ExceptionBuilder.RowNotInTheTable();
 
            if (relation.DataSet != _table.DataSet)
                throw ExceptionBuilder.RelationForeignRow(); 
 
            if (relation.ChildKey.Table != _table)
                throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName); 

            return DataRelation.GetParentRow(relation.ParentKey, relation.ChildKey, this, version);
        }
        // a multiple nested child table's row can have only one non-null FK per row. So table has multiple 
        // parents, but a row can have only one parent. Same nested row cannot below to 2 parent rows.
        internal DataRow GetNestedParentRow(DataRowVersion version) { 
            // 1) Walk over all FKs and get the non-null. 2) Get the relation. 3) Get the parent Row. 
            DataRelation[] nestedParentRelations = _table.NestedParentRelations;
            foreach(DataRelation rel in nestedParentRelations) { 
                if (rel == null) // don't like this but done for backward code compatability
                    continue;
                if (rel.ParentTable == _table) // self-nested table
                    this.CheckForLoops(rel); 
                DataRow row = this.GetParentRow(rel, version);
                if (row != null) { 
                    return row; 
                }
            } 
            return null;// Rule 1: At all times, only ONE FK  "(in a row) can be non-Null

        }
        // No Nested in 1-many 

        ///  
        ///    [To be supplied.] 
        /// 
        public DataRow[] GetParentRows(string relationName) { 
            return GetParentRows(_table.ParentRelations[relationName], DataRowVersion.Default);
        }

        ///  
        ///    [To be supplied.]
        ///  
        public DataRow[] GetParentRows(string relationName, DataRowVersion version) { 
            return GetParentRows(_table.ParentRelations[relationName], version);
        } 

        /// 
        ///    
        ///       Gets the parent rows of this  using the specified  . 
        ///    
        ///  
        public DataRow[] GetParentRows(DataRelation relation) { 
            return GetParentRows(relation, DataRowVersion.Default);
        } 

        /// 
        ///    
        ///       Gets the parent rows of this  using the specified  . 
        ///    
        ///  
        public DataRow[] GetParentRows(DataRelation relation, DataRowVersion version) { 
            if (relation == null)
                return _table.NewRowArray(0); 

            //if (-1 == rowID)
            //    throw ExceptionBuilder.RowNotInTheTable();
 
            if (relation.DataSet != _table.DataSet)
                throw ExceptionBuilder.RowNotInTheDataSet(); 
 
            if (relation.ChildKey.Table != _table)
                throw ExceptionBuilder.GetParentRowTableMismatch(relation.ChildTable.TableName, _table.TableName); 

            return DataRelation.GetParentRows(relation.ParentKey, relation.ChildKey, this, version);
        }
 
        internal object[] GetColumnValues(DataColumn[] columns) {
            return GetColumnValues(columns, DataRowVersion.Default); 
        } 

        internal object[] GetColumnValues(DataColumn[] columns, DataRowVersion version) { 
            DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
            return GetKeyValues(key, version);
        }
 
        internal object[] GetKeyValues(DataKey key) {
            int record = GetDefaultRecord(); 
            return key.GetKeyValues(record); 
        }
 
        internal object[] GetKeyValues(DataKey key, DataRowVersion version) {
            int record = GetRecordFromVersion(version);
            return key.GetKeyValues(record);
        } 

        internal int GetCurrentRecordNo() { 
            if (newRecord == -1) 
                throw ExceptionBuilder.NoCurrentData();
            return newRecord; 
        }

        internal int GetDefaultRecord() {
            if (tempRecord != -1) 
                return tempRecord;
            if (newRecord != -1) { 
                return newRecord; 
            }
            // If row has oldRecord - this is deleted row. 
            if (oldRecord == -1)
                throw ExceptionBuilder.RowRemovedFromTheTable();
            else
                throw ExceptionBuilder.DeletedRowInaccessible(); 
        }
 
        internal int GetOriginalRecordNo() { 
            if (oldRecord == -1)
                throw ExceptionBuilder.NoOriginalData(); 
            return oldRecord;
        }

        private int GetProposedRecordNo() { 
            if (tempRecord == -1)
                throw ExceptionBuilder.NoProposedData(); 
            return tempRecord; 
        }
 
        internal int GetRecordFromVersion(DataRowVersion version) {
            switch (version) {
                case DataRowVersion.Original:
                    return GetOriginalRecordNo(); 
                case DataRowVersion.Current:
                    return GetCurrentRecordNo(); 
                case DataRowVersion.Proposed: 
                    return GetProposedRecordNo();
                case DataRowVersion.Default: 
                    return GetDefaultRecord();
                default:
                    throw ExceptionBuilder.InvalidRowVersion();
            } 
        }
 
        internal DataRowVersion GetDefaultRowVersion(DataViewRowState viewState) { 
            if (oldRecord == newRecord) {
                if (oldRecord == -1) { 
                    // should be DataView.addNewRow
                    return DataRowVersion.Default;
                }
                Debug.Assert(0 != (DataViewRowState.Unchanged & viewState), "not DataViewRowState.Unchanged"); 
                return DataRowVersion.Default;
            } 
            else if (oldRecord == -1) { 
                Debug.Assert(0 != (DataViewRowState.Added & viewState), "not DataViewRowState.Added");
                return DataRowVersion.Default; 
            }
            else if (newRecord == -1) {
                Debug.Assert(0 != (DataViewRowState.Deleted & viewState), "not DataViewRowState.Deleted");
                return DataRowVersion.Original; 
            }
            else if (0 != (DataViewRowState.ModifiedCurrent & viewState)) { 
                return DataRowVersion.Default; 
            }
            Debug.Assert(0 != (DataViewRowState.ModifiedOriginal & viewState), "not DataViewRowState.ModifiedOriginal"); 
            return DataRowVersion.Original;
        }

        internal DataViewRowState GetRecordState(int record) { 
            if (record == -1)
                return DataViewRowState.None; 
            if (record == oldRecord && record == newRecord) 
                return DataViewRowState.Unchanged;
            if (record == oldRecord) 
                return(newRecord != -1) ? DataViewRowState.ModifiedOriginal : DataViewRowState.Deleted;
            if (record == newRecord)
                return(oldRecord != -1) ? DataViewRowState.ModifiedCurrent : DataViewRowState.Added;
            return DataViewRowState.None; 
        }
 
        internal bool HasKeyChanged(DataKey key) { 
            return HasKeyChanged(key, DataRowVersion.Current, DataRowVersion.Proposed);
        } 

        internal bool HasKeyChanged(DataKey key, DataRowVersion version1, DataRowVersion version2) {
            if (!HasVersion(version1) || !HasVersion(version2))
                return true; 
            return !key.RecordsEqual(GetRecordFromVersion(version1), GetRecordFromVersion(version2));
        } 
 
        /// 
        ///     
        ///       Gets a value indicating whether a specified version exists.
        ///    
        /// 
        public bool HasVersion(DataRowVersion version) { 
            switch (version) {
                case DataRowVersion.Original: 
                    return(oldRecord != -1); 
                case DataRowVersion.Current:
                    return(newRecord != -1); 
                case DataRowVersion.Proposed:
                    return(tempRecord != -1);
                case DataRowVersion.Default:
                    return(tempRecord != -1 || newRecord != -1); 
                default:
                    throw ExceptionBuilder.InvalidRowVersion(); 
            } 
        }
 
        internal bool HasChanges() {
            if (!HasVersion(DataRowVersion.Original) || !HasVersion(DataRowVersion.Current)) {
                return true; // if does not have original, its added row, if does not have current, its deleted row so it has changes
            } 
            foreach(DataColumn dc in Table.Columns) {
                if (dc.Compare(oldRecord, newRecord) != 0) { 
                    return true; 
                }
            } 
            return false;
        }

        internal bool HaveValuesChanged(DataColumn[] columns) { 
            return HaveValuesChanged(columns, DataRowVersion.Current, DataRowVersion.Proposed);
        } 
 
        internal bool HaveValuesChanged(DataColumn[] columns, DataRowVersion version1, DataRowVersion version2) {
            for (int i = 0; i < columns.Length; i++) { 
                CheckColumn(columns[i]);
            }
            DataKey key = new DataKey(columns, false); // temporary key, don't copy columns
            return HasKeyChanged(key, version1, version2); 
        }
 
        ///  
        ///    
        ///       Gets 
        ///       a value indicating whether the column at the specified index contains a
        ///       null value.
        ///    
        ///  
        public bool IsNull(int columnIndex) {
            DataColumn column = _columns[columnIndex]; 
            int record = GetDefaultRecord(); 
            return column.IsNull(record);
        } 

        /// 
        ///    
        ///       Gets a value indicating whether the named column contains a null value. 
        ///    
        ///  
        public bool IsNull(string columnName) { 
            DataColumn column = GetDataColumn(columnName);
            int record = GetDefaultRecord(); 
            return column.IsNull(record);
        }

        ///  
        ///    
        ///       Gets a value indicating whether the specified  
        ///       contains a null value. 
        ///    
        ///  
        public bool IsNull(DataColumn column) {
            CheckColumn(column);
            int record = GetDefaultRecord();
            return column.IsNull(record); 
        }
 
        ///  
        ///    [To be supplied.]
        ///  
        public bool IsNull(DataColumn column, DataRowVersion version) {
            CheckColumn(column);
            int record = GetRecordFromVersion(version);
            return column.IsNull(record); 
        }
 
        ///  
        ///    
        ///       Rejects all changes made to the row since  
        ///       was last called.
        ///    
        /// 
        public void RejectChanges() { 
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, " %d#\n", ObjectID); 
            try { 
                if (this.RowState != DataRowState.Detached) {
                    if (_columns.ColumnsImplementingIChangeTrackingCount != _columns.ColumnsImplementingIRevertibleChangeTrackingCount) { 
                        foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) {
                            if (!dc.ImplementsIRevertibleChangeTracking) {
                                object value = null;
                                if (this.RowState != DataRowState.Deleted) 
                                    value = this[dc];
                                else 
                                    value = this[dc, DataRowVersion.Original]; 
                                if (DBNull.Value != value){
                                    if (((IChangeTracking)value).IsChanged) { 
                                        throw ExceptionBuilder.UDTImplementsIChangeTrackingButnotIRevertible(dc.DataType.AssemblyQualifiedName);
                                    }
                                }
                            } 
                        }
                    } 
                    foreach(DataColumn dc in _columns.ColumnsImplementingIChangeTracking) { 
                        object value = null;
                         if (this.RowState != DataRowState.Deleted) 
                            value = this[dc];
                         else
                            value = this[dc, DataRowVersion.Original];
                        if (DBNull.Value != value) { 
                            IChangeTracking tracking = (IChangeTracking)value;
                            if (tracking.IsChanged) { 
                                ((IRevertibleChangeTracking)value).RejectChanges(); 
                            }
                        } 
                    }
                }
                _table.RollbackRow(this);
            } 
            finally {
                Bid.ScopeLeave(ref hscp); 
            } 
        }
 
        internal void ResetLastChangedColumn() {
            _lastChangedColumn = null;
            _countColumnChange = 0;
        } 

        internal void SetKeyValues(DataKey key, object[] keyValues) { 
            bool fFirstCall = true; 
            bool immediate = (tempRecord == -1);
 
            for (int i = 0; i < keyValues.Length; i++) {
                object value = this[key.ColumnsReference[i]];
                if (!value.Equals(keyValues[i])) {
                    if (immediate && fFirstCall) { 
                        fFirstCall = false;
                        BeginEditInternal(); 
                    } 
                    this[key.ColumnsReference[i]] = keyValues[i];
                } 
            }
            if (!fFirstCall)
                EndEdit();
        } 

        ///  
        ///     
        ///       Sets the specified column's value to a null value.
        ///     
        /// 
        protected void SetNull(DataColumn column) {
            this[column] = DBNull.Value;
        } 

        internal void SetNestedParentRow(DataRow parentRow, bool setNonNested) { 
            if (parentRow == null) { 
                SetParentRowToDBNull();
                return; 
            }

            foreach (DataRelation relation in _table.ParentRelations) {
                if (relation.Nested || setNonNested) { 
                    if (relation.ParentKey.Table == parentRow._table) {
                        object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey); 
                        this.SetKeyValues(relation.ChildKey, parentKeyValues); 

                        if (relation.Nested) { 
                            if (parentRow._table == _table)
                                this.CheckForLoops(relation);
                            else
                                this.GetParentRow(relation); 
                        }
                    } 
                } 
            }
        } 
        /// 
        ///    [To be supplied.]
        /// 
        public void SetParentRow(DataRow parentRow) { 
            SetNestedParentRow(parentRow, true);
        } 
 
        /// 
        ///     
        ///       Sets current row's parent row with specified relation.
        ///    
        /// 
        public void SetParentRow(DataRow parentRow, DataRelation relation) { 
            if (relation == null) {
                SetParentRow(parentRow); 
                return; 
            }
 
            if (parentRow == null) {
                SetParentRowToDBNull(relation);
                return;
            } 

            //if (-1 == rowID) 
            //    throw ExceptionBuilder.ChildRowNotInTheTable(); 

            //if (-1 == parentRow.rowID) 
            //    throw ExceptionBuilder.ParentRowNotInTheTable();

            if (_table.DataSet != parentRow._table.DataSet)
                throw ExceptionBuilder.ParentRowNotInTheDataSet(); 

            if (relation.ChildKey.Table != _table) 
                throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName); 

            if (relation.ParentKey.Table != parentRow._table) 
                throw ExceptionBuilder.SetParentRowTableMismatch(relation.ParentKey.Table.TableName, parentRow._table.TableName);

            object[] parentKeyValues = parentRow.GetKeyValues(relation.ParentKey);
            this.SetKeyValues(relation.ChildKey, parentKeyValues); 
        }
 
        internal void SetParentRowToDBNull() { 
            //if (-1 == rowID)
            //    throw ExceptionBuilder.ChildRowNotInTheTable(); 

            foreach (DataRelation relation in _table.ParentRelations)
                SetParentRowToDBNull(relation);
        } 

        internal void SetParentRowToDBNull(DataRelation relation) { 
            Debug.Assert(relation != null, "The relation should not be null here."); 

            //if (-1 == rowID) 
            //    throw ExceptionBuilder.ChildRowNotInTheTable();

            if (relation.ChildKey.Table != _table)
                throw ExceptionBuilder.SetParentRowTableMismatch(relation.ChildKey.Table.TableName, _table.TableName); 

 
            object[] parentKeyValues = new object[1]; 
            parentKeyValues[0] = DBNull.Value;
            this.SetKeyValues(relation.ChildKey, parentKeyValues); 
        }
        public void SetAdded(){
            if (this.RowState == DataRowState.Unchanged) {
                _table.SetOldRecord(this, -1); 
            }
            else { 
                throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged(); 
            }
        } 

        public void SetModified(){
            if (this.RowState == DataRowState.Unchanged) {
                tempRecord = _table.NewRecord(newRecord); 
                if (tempRecord != -1) {
                    // suppressing the ensure property changed because no values have changed 
                    _table.SetNewRecord(this, tempRecord, suppressEnsurePropertyChanged: true); 
                }
            } 
            else {
                throw ExceptionBuilder.SetAddedAndModifiedCalledOnnonUnchanged();
            }
        } 

/* 
    RecordList contains the empty column storage needed. We need to copy the existing record values into this storage. 
*/
        internal int CopyValuesIntoStore(ArrayList storeList, ArrayList nullbitList, int storeIndex) { 
            int recordCount = 0;
            if (oldRecord != -1) {//Copy original record for the row in Unchanged, Modified, Deleted state.
                for (int i = 0; i < _columns.Count; i++) {
                    _columns[i].CopyValueIntoStore(oldRecord, storeList[i], (BitArray) nullbitList[i], storeIndex); 
                }
                recordCount++; 
                storeIndex++; 
            }
 
            DataRowState state = RowState;
            if ((DataRowState.Added == state) || (DataRowState.Modified == state)) { //Copy current record for the row in Added, Modified state.
                for (int i = 0; i < _columns.Count; i++) {
                    _columns[i].CopyValueIntoStore(newRecord, storeList[i], (BitArray) nullbitList[i], storeIndex); 
                }
                recordCount++; 
                storeIndex++; 
            }
 
            if (-1 != tempRecord) {//Copy temp record for the row in edit mode.
                for (int i = 0; i < _columns.Count; i++) {
                    _columns[i].CopyValueIntoStore(tempRecord, storeList[i], (BitArray)nullbitList[i], storeIndex);
                } 
                recordCount++;
                storeIndex++; 
            } 
            return recordCount;
        } 
 		
        [Conditional("DEBUG")]
        private void VerifyValueFromStorage(DataColumn column, DataRowVersion version, object valueFromStorage) {
            if (column.DataExpression != null && !inChangingEvent && tempRecord == -1) 
            {
                // for unchanged rows, check current if original is asked for. 
                // this is because by design, there is only single storage for an unchanged row. 
                if (version == DataRowVersion.Original && oldRecord == newRecord) {
                    version = DataRowVersion.Current; 
                }
					
                Debug.Assert(valueFromStorage.Equals(column.DataExpression.Evaluate(this, version)),
                    "Value from storage does lazily computed expression value"); 
            }
        } 
    } 

    public sealed class DataRowBuilder { 
        internal readonly DataTable   _table;
        internal int                  _record;

        internal DataRowBuilder(DataTable table, int record) { 
            _table = table;
            _record = record; 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.