File: fx\src\data\System\Data\DataTable.cs
Project: ndp\System.Data.csproj (System.Data)
//------------------------------------------------------------------------------
// <copyright file="DataTable.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace System.Data {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Threading;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using System.Data.Common;
    using System.Runtime.Versioning;
    using System.Runtime.CompilerServices;
 
    /// <devdoc>
    ///    <para>Represents one table of in-memory data.</para>
    /// </devdoc>
    [
    ToolboxItem(false),
    DesignTimeVisible(false),
    DefaultProperty("TableName"),
    Editor("Microsoft.VSDesigner.Data.Design.DataTableEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
    DefaultEvent("RowChanging"),
    XmlSchemaProvider("GetDataTableSchema"),
    Serializable
    ]
    public class DataTable : MarshalByValueComponent, System.ComponentModel.IListSource, ISupportInitializeNotification, ISerializable, IXmlSerializable{
        private DataSet dataSet;
        private DataView defaultView = null;
 
        // rows
        /// <summary>
        /// Monotonically increasing number representing the order <see cref="DataRow"/> have been added to <see cref="DataRowCollection"/>.
        /// </summary>
        /// <remarks>This limits <see cref="DataRowCollection.Add(DataRow)"/> to <see cref="Int32.MaxValue"/> operations.</remarks>
        internal long nextRowID;
        internal readonly DataRowCollection rowCollection;
 
        // columns
        internal readonly DataColumnCollection columnCollection;
 
        // constraints
        private readonly ConstraintCollection constraintCollection;
 
        //SimpleContent implementation
        private int elementColumnCount = 0;
 
        // relations
        internal DataRelationCollection parentRelationsCollection;
        internal DataRelationCollection childRelationsCollection;
 
        // RecordManager
        internal readonly RecordManager recordManager;
 
        // index mgmt
        internal readonly List<Index> indexes;
 
        private List<Index> shadowIndexes;
        private int shadowCount;
 
        // props
        internal PropertyCollection extendedProperties = null;
        private string tableName = "";
        internal string tableNamespace = null;
        private string tablePrefix = "";
        internal DataExpression displayExpression;
        internal bool fNestedInDataset = true;
 
        // globalization stuff
        private CultureInfo _culture;
        private bool _cultureUserSet;
        private CompareInfo _compareInfo;
        private CompareOptions _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
        private IFormatProvider _formatProvider;
        private StringComparer _hashCodeProvider;
        private bool _caseSensitive;
        private bool _caseSensitiveUserSet;
 
        // XML properties
        internal string encodedTableName;           // For XmlDataDocument only
        internal DataColumn xmlText;            // text values of a complex xml element
        internal DataColumn _colUnique;
        internal bool textOnly = false;         // the table has only text value with possible attributes
        internal decimal minOccurs = 1;    // default = 1
        internal decimal maxOccurs = 1;    // default = 1
        internal bool repeatableElement = false;
        private object typeName = null;
 
        // primary key info
        private readonly static Int32[] zeroIntegers = new Int32[0];
        internal readonly static DataColumn[] zeroColumns = new DataColumn[0];
        internal readonly static DataRow[] zeroRows = new DataRow[0];
        internal UniqueConstraint primaryKey;
        internal readonly static IndexField[] zeroIndexField = new IndexField[0];
        internal IndexField[] _primaryIndex = zeroIndexField;
        private DataColumn[] delayedSetPrimaryKey = null;
 
        // Loading Schema and/or Data related optimization
        private Index loadIndex;
        private Index loadIndexwithOriginalAdded = null;
        private Index loadIndexwithCurrentDeleted = null;
        private int _suspendIndexEvents;
 
        private bool savedEnforceConstraints = false;
        private bool inDataLoad = false;
        private bool initialLoad;
        private bool schemaLoading = false;
        private bool enforceConstraints = true;
        internal bool _suspendEnforceConstraints = false;
 
        protected internal bool fInitInProgress = false;
        private bool inLoad = false;
        internal bool fInLoadDiffgram = false;
 
        private byte _isTypedDataTable; // 0 == unknown, 1 = yes, 2 = No
        private DataRow[] EmptyDataRowArray;
 
 
        // Property Descriptor Cache for DataBinding
        private PropertyDescriptorCollection propertyDescriptorCollectionCache = null;
 
        // Cache for relation that has this table as nested child table.
        private static readonly DataRelation[] EmptyArrayDataRelation = new DataRelation[0];
        private DataRelation[] _nestedParentRelations = EmptyArrayDataRelation;
 
        // Dependent column list for expression evaluation
        internal List<DataColumn> dependentColumns = null;
 
        // events
        private bool mergingData = false;
        private DataRowChangeEventHandler onRowChangedDelegate;
        private DataRowChangeEventHandler onRowChangingDelegate;
        private DataRowChangeEventHandler onRowDeletingDelegate;
        private DataRowChangeEventHandler onRowDeletedDelegate;
        private DataColumnChangeEventHandler onColumnChangedDelegate;
        private DataColumnChangeEventHandler onColumnChangingDelegate;
 
        private DataTableClearEventHandler onTableClearingDelegate;
        private DataTableClearEventHandler onTableClearedDelegate;
        private DataTableNewRowEventHandler onTableNewRowDelegate;
 
        private PropertyChangedEventHandler onPropertyChangingDelegate;
 
        private System.EventHandler  onInitialized;
 
 
        // misc
        private readonly DataRowBuilder rowBuilder;
        private const String KEY_XMLSCHEMA = "XmlSchema";
        private const String KEY_XMLDIFFGRAM = "XmlDiffGram";
        private const String KEY_NAME = "TableName";
 
        internal readonly List<DataView> delayedViews = new List<DataView>();
        private readonly List<DataViewListener> _dataViewListeners = new List<DataViewListener>();
 
//        private bool serializeHierarchy = false;
        internal Hashtable rowDiffId = null;
        internal readonly ReaderWriterLock indexesLock = new ReaderWriterLock();
        internal int ukColumnPositionForInference= -1;
 
        // default remoting format is Xml
        private SerializationFormat _remotingFormat = SerializationFormat.Xml;
 
        private static int _objectTypeCount; // Bid counter
        private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
 
        /// <devdoc>
        /// <para>Initializes a new instance of the <see cref='System.Data.DataTable'/> class with no arguments.</para>
        /// </devdoc>
        public DataTable() {
            GC.SuppressFinalize(this);
            Bid.Trace("<ds.DataTable.DataTable|API> %d#\n", ObjectID);
            nextRowID = 1;
            recordManager = new RecordManager(this);
 
            _culture = CultureInfo.CurrentCulture;
            this.columnCollection = new DataColumnCollection(this);
            this.constraintCollection = new ConstraintCollection(this);
            this.rowCollection = new DataRowCollection(this);
            this.indexes = new List<Index>();
 
            rowBuilder = new DataRowBuilder(this, -1);
        }
 
        /// <devdoc>
        /// <para>Intitalizes a new instance of the <see cref='System.Data.DataTable'/> class with the specified table
        ///    name.</para>
        /// </devdoc>
        public DataTable(string tableName) : this() {
            this.tableName = tableName == null ? "" : tableName;
        }
 
        public DataTable(string tableName, string tableNamespace) : this(tableName) {
            this.Namespace = tableNamespace;
        }
 
//        Deserialize the table from binary/xml stream.
        protected DataTable(SerializationInfo info, StreamingContext context) : this()
        {
            bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
            SerializationFormat remotingFormat = SerializationFormat.Xml;
            SerializationInfoEnumerator e = info.GetEnumerator();
            while (e.MoveNext()) {
                switch(e.Name) {
                    case "DataTable.RemotingFormat" : //DataTable.RemotingFormat does not exist in V1/V1.1 versions
                    remotingFormat = (SerializationFormat)e.Value;
                    break;
                }
            }
 
            DeserializeDataTable(info, context, isSingleTable, remotingFormat);
        }
 
        [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
            SerializationFormat remotingFormat = RemotingFormat;
            bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
            SerializeDataTable(info, context, isSingleTable, remotingFormat);
        }
 
//        Serialize the table schema and data.
        private void SerializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
            info.AddValue("DataTable.RemotingVersion", new Version(2, 0));
 
            // SqlHotFix 299, SerializationFormat enumeration types don't exist in V1.1 SP1
            if (SerializationFormat.Xml != remotingFormat) {
                info.AddValue("DataTable.RemotingFormat", remotingFormat);
            }
 
            if (remotingFormat != SerializationFormat.Xml) {//Binary
                SerializeTableSchema(info, context, isSingleTable);
                if (isSingleTable) {
                    SerializeTableData(info, context, 0);
                }
            } else {//XML/V1.0/V1.1
                string tempDSNamespace = "";
                Boolean fCreatedDataSet = false;
 
                if (dataSet == null) {
                    DataSet ds = new DataSet("tmpDataSet");
                    // if user set values on DataTable, it isn't necessary
                    // to set them on the DataSet because they won't be inherited
                    // but it is simpler to set them in both places
 
                    // if user did not set values on DataTable, it is required
                    // to set them on the DataSet so the table will inherit
                    // the value already on the Datatable
                    ds.SetLocaleValue(_culture, _cultureUserSet);
                    ds.CaseSensitive = this.CaseSensitive;
                    ds.namespaceURI  = this.Namespace;
                    Debug.Assert(ds.RemotingFormat == SerializationFormat.Xml, "RemotingFormat must be SerializationFormat.Xml");
                    ds.Tables.Add(this);
                    fCreatedDataSet = true;
                } else {
                    tempDSNamespace = this.DataSet.Namespace;
                    this.DataSet.namespaceURI = this.Namespace; //this.DataSet.Namespace = this.Namespace; ??
                }
 
                info.AddValue(KEY_XMLSCHEMA, dataSet.GetXmlSchemaForRemoting(this));
                info.AddValue(KEY_XMLDIFFGRAM, dataSet.GetRemotingDiffGram(this));
 
                if (fCreatedDataSet) {
                    dataSet.Tables.Remove(this);
                }
                else{
                    dataSet.namespaceURI  = tempDSNamespace;
                }
            }
        }
 
//        Deserialize the table schema and data.
        internal void DeserializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
            if (remotingFormat != SerializationFormat.Xml) {//Binary
                DeserializeTableSchema(info, context, isSingleTable);
                if (isSingleTable) {
                    DeserializeTableData(info, context, 0);
                    this.ResetIndexes();
                }
            } else {//XML/V1.0/V1.1
                string strSchema = (String)info.GetValue(KEY_XMLSCHEMA, typeof(System.String));
                string strData = (String)info.GetValue(KEY_XMLDIFFGRAM, typeof(System.String));
 
                if (strSchema != null) {
                    DataSet ds = new DataSet();
                    // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
                    ds.ReadXmlSchema(new XmlTextReader( new StringReader( strSchema ) ) );
 
                    Debug.Assert(ds.Tables.Count == 1, "There should be exactly 1 table here");
                    DataTable table = ds.Tables[0];
                    table.CloneTo(this, null, false);// WebData 111656
                    //this is to avoid the cascading rules in the namespace
                    this.Namespace = table.Namespace;
 
                    if (strData != null) {
                        ds.Tables.Remove(ds.Tables[0]);
                        ds.Tables.Add(this);
                        ds.ReadXml(new XmlTextReader( new StringReader( strData ) ), XmlReadMode.DiffGram);
                        ds.Tables.Remove(this);
                    }
                }
            }
        }
 
//        Serialize the columns
        internal void SerializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
            //DataTable basic  properties
            info.AddValue("DataTable.TableName", TableName);
            info.AddValue("DataTable.Namespace", Namespace);
            info.AddValue("DataTable.Prefix", Prefix);
            info.AddValue("DataTable.CaseSensitive", _caseSensitive);
            info.AddValue("DataTable.caseSensitiveAmbient", !_caseSensitiveUserSet);
            info.AddValue("DataTable.LocaleLCID", Locale.LCID);
            info.AddValue("DataTable.MinimumCapacity", recordManager.MinimumCapacity);
            //info.AddValue("DataTable.DisplayExpression", DisplayExpression);
 
            //DataTable state internal properties
            info.AddValue("DataTable.NestedInDataSet", fNestedInDataset);
            info.AddValue("DataTable.TypeName", TypeName.ToString());
            info.AddValue("DataTable.RepeatableElement", repeatableElement);
 
 
            //ExtendedProperties
            info.AddValue("DataTable.ExtendedProperties", ExtendedProperties);
 
            //Columns
            info.AddValue("DataTable.Columns.Count", Columns.Count);
 
            //Check for closure of expression in case of single table.
            if (isSingleTable) {
                List<DataTable> list = new List<DataTable>();
                list.Add(this);
                if (!CheckForClosureOnExpressionTables(list))
                    throw ExceptionBuilder.CanNotRemoteDataTable();
            }
 
            IFormatProvider formatProvider = CultureInfo.InvariantCulture;
            for (int i = 0; i < Columns.Count; i++) {
                //DataColumn basic properties
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i), Columns[i].ColumnName);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i), Columns[i]._columnUri);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i), Columns[i].Prefix);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), Columns[i].ColumnMapping);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i), Columns[i].AllowDBNull);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i), Columns[i].AutoIncrement);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i), Columns[i].AutoIncrementStep);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i), Columns[i].AutoIncrementSeed);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i), Columns[i].Caption);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), Columns[i].DefaultValue);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i), Columns[i].ReadOnly);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i), Columns[i].MaxLength);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), Columns[i].DataType);
 
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), Columns[i].XmlDataType);
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), Columns[i].SimpleType);
 
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), Columns[i].DateTimeMode);
 
                //DataColumn internal state properties
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), Columns[i].AutoIncrementCurrent);
 
                //Expression
                if (isSingleTable) {
                    info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i), Columns[i].Expression);
                }
 
                //ExtendedProperties
                info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), Columns[i].extendedProperties);
            }
 
            //Constraints
            if (isSingleTable) {
                SerializeConstraints(info, context, 0, false);
            }
        }
 
//        Deserialize all the Columns
        internal void DeserializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
            //DataTable basic properties
            tableName = info.GetString("DataTable.TableName");
            tableNamespace = info.GetString("DataTable.Namespace");
            tablePrefix = info.GetString("DataTable.Prefix");
 
            bool caseSensitive = info.GetBoolean("DataTable.CaseSensitive");
            SetCaseSensitiveValue(caseSensitive, true, false);
            _caseSensitiveUserSet = !info.GetBoolean("DataTable.caseSensitiveAmbient");
 
            int lcid = (int)info.GetValue("DataTable.LocaleLCID", typeof(int));
            CultureInfo culture = new CultureInfo(lcid);
            SetLocaleValue(culture, true, false);
            _cultureUserSet = true;
 
 
            MinimumCapacity = info.GetInt32("DataTable.MinimumCapacity");
            //DisplayExpression = info.GetString("DataTable.DisplayExpression");
 
            //DataTable state internal properties
            fNestedInDataset = (bool) info.GetBoolean("DataTable.NestedInDataSet");
            string tName = info.GetString("DataTable.TypeName");
            typeName =  new XmlQualifiedName(tName);
            repeatableElement = info.GetBoolean("DataTable.RepeatableElement");
 
            //ExtendedProperties
            extendedProperties = (PropertyCollection) info.GetValue("DataTable.ExtendedProperties", typeof(PropertyCollection));
 
            //Columns
            int colCount = info.GetInt32("DataTable.Columns.Count");
            string [] expressions = new string[colCount];
            Debug.Assert(Columns.Count == 0, "There is column in Table");
 
            IFormatProvider formatProvider = CultureInfo.InvariantCulture;
            for (int i = 0; i < colCount; i++) {
                DataColumn dc = new DataColumn();
 
                //DataColumn public state properties
                dc.ColumnName = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i));
                dc._columnUri = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i));
                dc.Prefix = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i));
 
                dc.DataType = (Type) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), typeof(Type));
                dc.XmlDataType = (string) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), typeof(string));
                dc.SimpleType = (SimpleType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), typeof(SimpleType));
 
                dc.ColumnMapping = (MappingType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), typeof(MappingType));
                dc.DateTimeMode = (DataSetDateTime) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), typeof(DataSetDateTime));
 
                dc.AllowDBNull = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i));
                dc.AutoIncrement = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i));
                dc.AutoIncrementStep = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i));
                dc.AutoIncrementSeed = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i));
                dc.Caption = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i));
                dc.DefaultValue = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), typeof(object));
                dc.ReadOnly = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i));
                dc.MaxLength= info.GetInt32(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i));
 
                //DataColumn internal state properties
                dc.AutoIncrementCurrent = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), typeof(object));
 
                //Expression
                if (isSingleTable) {
                    expressions[i] = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i));
                }
 
                //ExtendedProperties
                dc.extendedProperties = (PropertyCollection) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), typeof(PropertyCollection));
                Columns.Add(dc);
            }
            if (isSingleTable) {
                for(int i = 0; i < colCount; i++) {
                    if (expressions[i] != null) {
                        Columns[i].Expression = expressions[i];
                    }
                }
            }
 
            //Constraints
            if (isSingleTable) {
                DeserializeConstraints(info, context, /*table index */ 0, /* serialize all constraints */false);// since single table, send table index as 0, meanwhile passing
                // false for 'allConstraints' means, handle all the constraint related to the table
            }
        }
 
/*
        Serialize constraints availabe on the table - note this function is marked internal because it is called by the DataSet deserializer.
        ***Schema for Serializing ArrayList of Constraints***
        Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
        Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
*/
        internal void SerializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
            if (allConstraints) {
                Debug.Assert(DataSet != null);
            }
 
            ArrayList constraintList = new ArrayList();
 
            for (int i = 0; i < Constraints.Count; i++) {
                Constraint c = Constraints[i];
 
                UniqueConstraint uc = c as UniqueConstraint;
                if (uc != null) {
                    int[] colInfo = new int[uc.Columns.Length];
                    for (int j = 0; j < colInfo.Length; j++) {
                        colInfo[j] = uc.Columns[j].Ordinal;
                    }
 
                    ArrayList list = new ArrayList();
                    list.Add("U");
                    list.Add(uc.ConstraintName);
                    list.Add(colInfo);
                    list.Add(uc.IsPrimaryKey);
                    list.Add(uc.ExtendedProperties);
 
                    constraintList.Add(list);
                } else {
                    ForeignKeyConstraint fk = c as ForeignKeyConstraint;
                    Debug.Assert(fk != null);
                    bool shouldSerialize = (allConstraints == true) || (fk.Table == this && fk.RelatedTable == this);
 
                    if (shouldSerialize) {
                        int[] parentInfo = new int[fk.RelatedColumns.Length + 1];
                        parentInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.RelatedTable) : 0;
                        for (int j = 1; j < parentInfo.Length; j++) {
                            parentInfo[j] = fk.RelatedColumns[j - 1].Ordinal;
                        }
 
                        int[] childInfo = new int[fk.Columns.Length + 1];
                        childInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.Table) : 0 ;   //Since the constraint is on the current table, this is the child table.
                        for (int j = 1; j < childInfo.Length; j++) {
                            childInfo[j] = fk.Columns[j - 1].Ordinal;
                        }
 
                        ArrayList list = new ArrayList();
                        list.Add("F");
                        list.Add(fk.ConstraintName);
                        list.Add(parentInfo);
                        list.Add(childInfo);
                        list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
                        list.Add(fk.ExtendedProperties);
 
                        constraintList.Add(list);
                    }
                }
            }
            info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), constraintList);
        }
 
/*
        Deserialize the constraints on the table.
        ***Schema for Serializing ArrayList of Constraints***
        Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
        Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
*/
        internal void DeserializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
            ArrayList constraintList = (ArrayList) info.GetValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), typeof(ArrayList));
 
            foreach (ArrayList list in constraintList) {
                string con = (string) list[0];
 
                if (con.Equals("U")) { //Unique Constraints
                    string constraintName = (string) list[1];
 
                    int[] keyColumnIndexes = (int[]) list[2];
                    bool isPrimaryKey = (bool) list[3];
                    PropertyCollection extendedProperties = (PropertyCollection) list[4];
 
                    DataColumn[] keyColumns = new DataColumn[keyColumnIndexes.Length];
                    for (int i = 0; i < keyColumnIndexes.Length; i++) {
                        keyColumns[i] = Columns[keyColumnIndexes[i]];
                    }
 
                    //Create the constraint.
                    UniqueConstraint uc = new UniqueConstraint(constraintName, keyColumns, isPrimaryKey);
                    uc.extendedProperties = extendedProperties;
 
                    //Add the unique constraint and it will in turn set the primary keys also if needed.
                    Constraints.Add(uc);
                } else { //ForeignKeyConstraints
                    Debug.Assert(con.Equals("F"));
 
                    string constraintName = (string) list[1];
                    int[] parentInfo = (int[]) list[2];
                    int[] childInfo = (int[]) list[3];
                    int[] rules = (int[]) list[4];
                    PropertyCollection extendedProperties = (PropertyCollection) list[5];
 
                    //ParentKey Columns.
                    DataTable parentTable = (allConstraints == false) ? this : this.DataSet.Tables[parentInfo[0]];
                    DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
                    for (int i = 0; i < parentkeyColumns.Length; i++) {
                        parentkeyColumns[i] = parentTable.Columns[parentInfo[i + 1]];
                    }
 
                    //ChildKey Columns.
                    DataTable childTable = (allConstraints == false) ? this : this.DataSet.Tables[childInfo[0]];
                    DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
                    for (int i = 0; i < childkeyColumns.Length; i++) {
                        childkeyColumns[i] = childTable.Columns[childInfo[i + 1]];
                    }
 
                    //Create the Constraint.
                    ForeignKeyConstraint fk = new ForeignKeyConstraint(constraintName, parentkeyColumns, childkeyColumns);
                    fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
                    fk.UpdateRule = (Rule) rules[1];
                    fk.DeleteRule = (Rule) rules[2];
                    fk.extendedProperties = extendedProperties;
 
                    //Add just the foreign key constraint without creating unique constraint.
                    Constraints.Add(fk, false);
                }
            }
        }
 
//        Serialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
        internal void SerializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
            int colCount = Columns.Count;
            for (int i = 0; i < colCount; i++) {
                info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i), Columns[i].Expression);
            }
        }
 
//        Deserialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
        internal void DeserializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
            int colCount = Columns.Count;
            for (int i = 0; i < colCount; i++) {
                string expr = info.GetString(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i));
                if (0 != expr.Length) {
                    Columns[i].Expression = expr;
                }
            }
        }
 
//        Serialize all the Rows.
        internal void SerializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
            //Cache all the column count, row count
            int colCount = Columns.Count;
            int rowCount = Rows.Count;
            int modifiedRowCount = 0;
            int editRowCount = 0;
 
            //Compute row states and assign the bits accordingly - 00[Unchanged], 01[Added], 10[Modifed], 11[Deleted]
            BitArray rowStates = new BitArray(rowCount * 3, false); //All bit flags are set to false on initialization of the BitArray.
            for (int i = 0; i < rowCount; i++) {
                int bitIndex = i * 3;
                DataRow row = Rows[i];
                DataRowState rowState = row.RowState;
                switch (rowState) {
                    case DataRowState.Unchanged:
                        //rowStates[bitIndex] = false;
                        //rowStates[bitIndex + 1] = false;
                        break;
                    case DataRowState.Added:
                        //rowStates[bitIndex] = false;
                        rowStates[bitIndex + 1] = true;
                        break;
                    case DataRowState.Modified:
                        rowStates[bitIndex] = true;
                        //rowStates[bitIndex + 1] = false;
                        modifiedRowCount++;
                        break;
                    case DataRowState.Deleted:
                        rowStates[bitIndex] = true;
                        rowStates[bitIndex + 1] = true;
                        break;
                    default:
                        throw ExceptionBuilder.InvalidRowState(rowState);
                }
                if (-1 != row.tempRecord) {
                    rowStates[bitIndex + 2] = true;
                    editRowCount++;
                }
            }
 
            //Compute the actual storage records that need to be created.
            int recordCount = rowCount + modifiedRowCount + editRowCount;
 
            //Create column storages.
            ArrayList storeList = new ArrayList();
            ArrayList nullbitList = new ArrayList();
            if (recordCount > 0) { //Create the storage only if have records.
                for (int i = 0; i < colCount; i++) {
                    object store = Columns[i].GetEmptyColumnStore(recordCount);
                    storeList.Add(store);
                    BitArray nullbits = new BitArray(recordCount);
                    nullbitList.Add(nullbits);
                }
            }
 
            //Copy values into column storages
            int recordsConsumed = 0;
            Hashtable rowErrors = new Hashtable();
            Hashtable colErrors = new Hashtable();
            for (int i = 0; i < rowCount; i++) {
                int recordsPerRow = Rows[i].CopyValuesIntoStore(storeList, nullbitList, recordsConsumed);
                GetRowAndColumnErrors(i, rowErrors, colErrors);
                recordsConsumed += recordsPerRow;
            }
 
            IFormatProvider formatProvider = CultureInfo.InvariantCulture;
            //Serialize all the computed values.
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex), rowCount);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex), recordCount);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), rowStates);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), storeList);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), nullbitList);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), rowErrors);
            info.AddValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), colErrors);
        }
 
//        Deserialize all the Rows.
        internal void DeserializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
            bool enforceConstraintsOrg = enforceConstraints;
            bool inDataLoadOrg = inDataLoad;
 
 
            try {
                enforceConstraints = false;
                inDataLoad = true;
                IFormatProvider formatProvider = CultureInfo.InvariantCulture;
                int rowCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex));
                int recordCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex));
                BitArray rowStates = (BitArray) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), typeof(BitArray));
                ArrayList storeList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), typeof(ArrayList));
                ArrayList nullbitList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), typeof(ArrayList));
                Hashtable rowErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), typeof(Hashtable));
                rowErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
                Hashtable colErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), typeof(Hashtable));
                colErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
 
 
                if (recordCount <= 0) { //No need for deserialization of the storage and errors if there are no records.
                    return;
                }
 
                //Point the record manager storage to the deserialized values.
                for (int i = 0; i < Columns.Count; i++) {
                    Columns[i].SetStorage(storeList[i], (BitArray) nullbitList[i]);
                }
 
                //Create rows and set the records appropriately.
                int recordIndex = 0;
                DataRow[] rowArr = new DataRow[recordCount];
                for (int i = 0; i < rowCount; i++) {
                    //Create a new row which sets old and new records to -1.
                    DataRow row = NewEmptyRow();
                    rowArr[recordIndex] = row;
                    int bitIndex = i * 3;
                    switch (ConvertToRowState(rowStates, bitIndex)) {
                        case DataRowState.Unchanged:
                            row.oldRecord = recordIndex;
                            row.newRecord = recordIndex;
                            recordIndex += 1;
                            break;
                        case DataRowState.Added:
                            row.oldRecord = -1;
                            row.newRecord = recordIndex;
                            recordIndex += 1;
                            break;
                        case DataRowState.Modified:
                            row.oldRecord = recordIndex;
                            row.newRecord = recordIndex + 1;
                            rowArr[recordIndex + 1] = row;
                            recordIndex += 2;
                            break;
                        case DataRowState.Deleted:
                            row.oldRecord = recordIndex;
                            row.newRecord = -1;
                            recordIndex += 1;
                            break;
                    }
                    if (rowStates[bitIndex + 2]) {
                        row.tempRecord = recordIndex;
                        rowArr[recordIndex] = row;
                        recordIndex += 1;
                    } else {
                        row.tempRecord = -1;
                    }
                    Rows.ArrayAdd(row);
                    row.rowID = nextRowID;
                    nextRowID++;
                    ConvertToRowError(i, rowErrors, colErrors);
                }
                recordManager.SetRowCache(rowArr);
                ResetIndexes();
            } finally {
                enforceConstraints = enforceConstraintsOrg;
                inDataLoad = inDataLoadOrg;
            }
        }
 
//        Constructs the RowState from the two bits in the bitarray.
        private DataRowState ConvertToRowState(BitArray bitStates, int bitIndex) {
            Debug.Assert(bitStates != null);
            Debug.Assert(bitStates.Length > bitIndex);
 
            bool b1 = bitStates[bitIndex];
            bool b2 = bitStates[bitIndex + 1];
 
            if (!b1 && !b2) {
                return DataRowState.Unchanged;
            } else if (!b1 && b2) {
                return DataRowState.Added;
            } else if (b1 && !b2) {
                return DataRowState.Modified;
            } else if (b1 && b2) {
                return DataRowState.Deleted;
            } else {
                throw ExceptionBuilder.InvalidRowBitPattern();
            }
        }
 
//        Get the error on the row and columns - Marked internal so that DataSet deserializer can call into this
        internal void GetRowAndColumnErrors(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
            Debug.Assert(Rows.Count > rowIndex);
            Debug.Assert(rowErrors != null);
            Debug.Assert(colErrors != null);
 
            DataRow row = Rows[rowIndex];
 
            if (row.HasErrors) {
                rowErrors.Add(rowIndex, row.RowError);
                DataColumn[] dcArr = row.GetColumnsInError();
                if (dcArr.Length > 0) {
                    int[] columnsInError = new int[dcArr.Length];
                    string[] columnErrors = new string[dcArr.Length];
                    for (int i = 0; i < dcArr.Length; i++) {
                        columnsInError[i] = dcArr[i].Ordinal;
                        columnErrors[i] = row.GetColumnError(dcArr[i]);
                    }
                    ArrayList list = new ArrayList();
                    list.Add(columnsInError);
                    list.Add(columnErrors);
                    colErrors.Add(rowIndex, list);
                }
            }
        }
 
//        Set the row and columns in error..
        private void ConvertToRowError(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
            Debug.Assert(Rows.Count > rowIndex);
            Debug.Assert(rowErrors != null);
            Debug.Assert(colErrors != null);
 
            DataRow row = Rows[rowIndex];
 
            if (rowErrors.ContainsKey(rowIndex)) {
                row.RowError = (string) rowErrors[rowIndex];
            }
            if (colErrors.ContainsKey(rowIndex)) {
                ArrayList list = (ArrayList) colErrors[rowIndex];
                int[] columnsInError = (int[]) list[0];
                string[] columnErrors = (string[]) list[1];
                Debug.Assert(columnsInError.Length == columnErrors.Length);
                for (int i = 0; i < columnsInError.Length; i++) {
                    row.SetColumnError(columnsInError[i], columnErrors[i]);
                }
            }
        }
 
        /// <devdoc>
        ///    <para>Indicates whether string comparisons within the table are case-sensitive.</para>
        /// </devdoc>
        [ResDescriptionAttribute(Res.DataTableCaseSensitiveDescr)]
        public bool CaseSensitive {
            get {
                //The following assert is valid except when calling DataSet.set_CaseSensitive which Validates constraints and failing here
                //Debug.Assert(_caseSensitiveUserSet || (null == dataSet) || (dataSet.CaseSensitive == _caseSensitive), "CaseSensitive mismatch");
                return _caseSensitive;
            }
            set {
                if (_caseSensitive != value) {
                    bool oldValue = _caseSensitive;
                    bool oldUserSet = _caseSensitiveUserSet;
                    _caseSensitive = value;
                    _caseSensitiveUserSet = true;
 
                    if (DataSet != null && !DataSet.ValidateCaseConstraint()) {
                        _caseSensitive = oldValue;
                        _caseSensitiveUserSet = oldUserSet;
                        throw ExceptionBuilder.CannotChangeCaseLocale();
                    }
                    SetCaseSensitiveValue(value, true, true);
                }
                _caseSensitiveUserSet = true;
            }
        }
 
        internal bool AreIndexEventsSuspended {
            get { return (0 < _suspendIndexEvents); }
        }
 
        internal void RestoreIndexEvents(bool forceReset) {
            Bid.Trace("<ds.DataTable.RestoreIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
            if (0 < _suspendIndexEvents) {
                _suspendIndexEvents--;
                if (0 == _suspendIndexEvents) {
                    Exception first = null;
                    SetShadowIndexes();
                    try{
                    // the length of shadowIndexes will not change
                    // but the array instance may change during
                    // events during Index.Reset
                        int numIndexes = shadowIndexes.Count;
                        for (int i = 0; i < numIndexes; i++) {
                            Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                            try {
                                if (forceReset || ndx.HasRemoteAggregate) {
                                    ndx.Reset(); // resets & fires
                                }
                                else {
                                    ndx.FireResetEvent(); // fire the Reset event we were firing
                                }
                            }
                            catch(Exception e) {
                                if (!ADP.IsCatchableExceptionType (e)) {
                                    throw;
                                }
                                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                                if (null == first) {
                                    first = e;
                                }
                            }
                        }
                        if (null != first) {
                            throw first;
                        }
                    }
                   finally {
                       RestoreShadowIndexes();
                   }
                }
            }
        }
 
        internal void SuspendIndexEvents() {
            Bid.Trace("<ds.DataTable.SuspendIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
            _suspendIndexEvents++;
        }
 
        [Browsable(false)]
        public bool IsInitialized {
            get {
                return !fInitInProgress;
            }
        }
 
        private bool IsTypedDataTable {
            get {
                switch (_isTypedDataTable) {
                case 0:
                    _isTypedDataTable = (byte)((this.GetType() != typeof(DataTable))? 1 : 2);
                    return (1 == _isTypedDataTable);
                case 1:
                    return true;
                default:
                    return false;
                }
            }
        }
 
        internal bool SetCaseSensitiveValue(bool isCaseSensitive, bool userSet, bool resetIndexes) {
            if (userSet || (!_caseSensitiveUserSet && (_caseSensitive != isCaseSensitive))) {
                _caseSensitive = isCaseSensitive;
                if (isCaseSensitive) {
                    _compareFlags = CompareOptions.None;
                }
                else {
                    _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
                }
                if (resetIndexes) {
                    ResetIndexes();
                    foreach (Constraint constraint in Constraints) {
                       constraint.CheckConstraint();
                    }
                }
                return true;
            }
            return false;
        }
 
 
        private void ResetCaseSensitive() {
            // this method is used design-time scenarios via reflection
            //   by the property grid context menu to show the Reset option or not
            SetCaseSensitiveValue((null != dataSet) && dataSet.CaseSensitive, true, true);
            _caseSensitiveUserSet = false;
        }
 
        internal bool ShouldSerializeCaseSensitive() {
            // this method is used design-time scenarios via reflection
            //   by the property grid to show the CaseSensitive property in bold or not
            //   by the code dom for persisting the CaseSensitive property or not
            return _caseSensitiveUserSet;
        }
 
        internal bool SelfNested {
            get {
                // Is this correct? if ((top[i].nestedParentRelation!= null) && (top[i].nestedParentRelation.ParentTable == top[i]))
                foreach(DataRelation rel in ParentRelations) {
                    if (rel.Nested && rel.ParentTable == this) {
                        return true;
                    }
                }
                return false;
            }
        }
/*        internal bool SelfNestedWithOneRelation {
            get {
                return (this.ParentRelations.Count == 1 && (this.ParentRelations[0].ParentTable == this));
            }
        }
*/
 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
        internal List<Index> LiveIndexes {
            get {
                if (!AreIndexEventsSuspended) {
                    for (int i = indexes.Count-1; 0 <= i; --i) {
                        Index index = indexes[i];
                        if (index.RefCount <= 1) {
                            index.RemoveRef();
                        }
                    }
                }
                return indexes;
            }
        }
 
        [
        DefaultValue(SerializationFormat.Xml)
        ]
        public SerializationFormat RemotingFormat {
            get {
                return _remotingFormat;
            }
            set {
                if (value != SerializationFormat.Binary && value != SerializationFormat.Xml) {
                    throw ExceptionBuilder.InvalidRemotingFormat(value);
                }
                // table can not have different format than its dataset, unless it is stand alone datatable
                if (this.DataSet != null && value != this.DataSet.RemotingFormat) {
                    throw ExceptionBuilder.CanNotSetRemotingFormat();
                }
                _remotingFormat = value;
            }
        }
 
// used to keep temporary state of unique Key posiotion to be added for inference only
        internal int UKColumnPositionForInference {
            get {
                return ukColumnPositionForInference;
            }
            set{
                ukColumnPositionForInference= value;
            }
        }
 
        /// <devdoc>
        /// <para>Gets the collection of child relations for this <see cref='System.Data.DataTable'/>.</para>
        /// </devdoc>
        [
        Browsable(false),
        ResDescriptionAttribute(Res.DataTableChildRelationsDescr),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public DataRelationCollection ChildRelations {
            get {
                if (childRelationsCollection == null)
                    childRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, false);
                return childRelationsCollection;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets the collection of columns that belong to this table.</para>
        /// </devdoc>
        [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableColumnsDescr)
        ]
        public DataColumnCollection Columns {
            get {
                return columnCollection;
            }
        }
 
        private void ResetColumns() {
            // this method is used design-time scenarios via reflection
            //   by the property grid context menu to show the Reset option or not
            Columns.Clear();
        }
 
        private CompareInfo CompareInfo {
            get {
                if (null == _compareInfo) {
                    _compareInfo = Locale.CompareInfo;
                }
                return _compareInfo;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets the collection of constraints maintained by this table.</para>
        /// </devdoc>
        [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableConstraintsDescr)
        ]
        public ConstraintCollection Constraints {
            get {
                return constraintCollection;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Resets the <see cref='System.Data.DataTable.Constraints'/> property to its default state.
        ///    </para>
        /// </devdoc>
        private void ResetConstraints() {
            Constraints.Clear();
        }
 
        /// <devdoc>
        /// <para>Gets the <see cref='System.Data.DataSet'/> that this table belongs to.</para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false), ResDescriptionAttribute(Res.DataTableDataSetDescr)]
        public DataSet DataSet {
            get {
                return dataSet;
            }
        }
 
        /// <devdoc>
        /// Internal method for setting the DataSet pointer.
        /// </devdoc>
        internal void SetDataSet(DataSet dataSet) {
            if (this.dataSet != dataSet) {
                this.dataSet = dataSet;
 
                // Inform all the columns of the dataset being set.
                DataColumnCollection   cols = Columns;
                for (int i = 0; i < cols.Count; i++)
                    cols[i].OnSetDataSet();
 
                if (this.DataSet != null) {
                    defaultView = null;
                }
                //Set the remoting format variable directly
                if (dataSet != null) {
                    _remotingFormat = dataSet.RemotingFormat;
                }
            }
        }
 
        /// <devdoc>
        ///    <para>Gets a customized view of the table which may include a
        ///       filtered view, or a cursor position.</para>
        /// </devdoc>
        [Browsable(false), ResDescriptionAttribute(Res.DataTableDefaultViewDescr)]
        public DataView DefaultView {
            get {
                DataView view = defaultView;
                if (null == view) {
                    if (null != dataSet) {
                        view = dataSet.DefaultViewManager.CreateDataView(this);
                    }
                    else {
                        view = new DataView(this, true);
                        view.SetIndex2("", DataViewRowState.CurrentRows, null, true);
                    }
                    // avoid HostProtectionAttribute(Synchronization=true) by not calling virtual methods from inside a lock
                    view = Interlocked.CompareExchange<DataView>(ref defaultView, view, null);
                    if (null == view) {
                        view = defaultView;
                    }
                }
                return view;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets or sets the expression that will return a value used to represent
        ///       this table in UI.</para>
        /// </devdoc>
        [
        DefaultValue(""),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableDisplayExpressionDescr)
        ]
        public string DisplayExpression {
            get {
                return DisplayExpressionInternal;
            }
            set {
                if (value != null && value.Length > 0) {
                    this.displayExpression = new DataExpression(this, value);
                }
                else {
                    this.displayExpression = null;
                }
            }
        }
        internal string DisplayExpressionInternal {
            get {
                return(displayExpression != null ? displayExpression.Expression : "");
            }
        }
 
        internal bool EnforceConstraints {
            get {
                if (SuspendEnforceConstraints) {
                    return false;
                }
                if (dataSet != null)
                    return dataSet.EnforceConstraints;
 
                return this.enforceConstraints;
            }
            set {
                if (dataSet == null && this.enforceConstraints != value) {
                    if (value)
                        EnableConstraints();
 
                    this.enforceConstraints = value;
                }
            }
        }
 
        internal bool SuspendEnforceConstraints {
            get {
                return _suspendEnforceConstraints ;
            }
            set {
                _suspendEnforceConstraints = value;
            }
        }
 
        internal void EnableConstraints()
        {
            bool errors = false;
            foreach (Constraint constr in Constraints)
            {
                if (constr is UniqueConstraint)
                    errors |= constr.IsConstraintViolated();
            }
 
            foreach (DataColumn column in Columns) {
                if (!column.AllowDBNull) {
                    errors |= column.IsNotAllowDBNullViolated();
                }
                if (column.MaxLength >= 0) {
                    errors |= column.IsMaxLengthViolated();
                }
            }
 
            if (errors) {
                this.EnforceConstraints = false;
                throw ExceptionBuilder.EnforceConstraint();
            }
        }
 
        /// <devdoc>
        ///    <para>Gets the collection of customized user information.</para>
        /// </devdoc>
        [
        ResCategoryAttribute(Res.DataCategory_Data),
        Browsable(false),
        ResDescriptionAttribute(Res.ExtendedPropertiesDescr)
        ]
        public PropertyCollection ExtendedProperties {
            get {
                if (extendedProperties == null) {
                    extendedProperties = new PropertyCollection();
                }
                return extendedProperties;
            }
        }
 
        internal IFormatProvider FormatProvider {
            get {
                // used for Formating/Parsing
                // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemglobalizationcultureinfoclassisneutralculturetopic.asp
                if (null == _formatProvider) {
                    CultureInfo culture = Locale;
                    if (culture.IsNeutralCulture) {
                        culture = CultureInfo.InvariantCulture;
                    }
                    _formatProvider = (IFormatProvider)culture;
                }
                return _formatProvider;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets a value indicating whether there are errors in any of the rows in any of
        ///       the tables of the <see cref='System.Data.DataSet'/> to which the table belongs.</para>
        /// </devdoc>
        [Browsable(false), ResDescriptionAttribute(Res.DataTableHasErrorsDescr)]
        public bool HasErrors {
            get {
                for (int i = 0; i < Rows.Count; i++) {
                    if (Rows[i].HasErrors) {
                        return true;
                    }
                }
                return false;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets or sets the locale information used to compare strings within the table.</para>
        ///    <para>Also used for locale sensitive, case,kana,width insensitive column name lookups</para>
        ///    <para>Also used for converting values to and from string</para>
        /// </devdoc>
        [ResDescriptionAttribute(Res.DataTableLocaleDescr)]
        public CultureInfo Locale {
            get {
                // used for Comparing not Formatting/Parsing
                Debug.Assert(null != _culture, "null culture");
                Debug.Assert(_cultureUserSet || (null == dataSet) || _culture.Equals(dataSet.Locale), "Locale mismatch");
                return _culture;
            }
            set {
                IntPtr hscp;
                Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Locale|API> %d#\n", ObjectID);
                try {
                    bool userSet = true;
                    if (null == value)  {
                        // reset Locale to inherit from DataSet
                        userSet = false;
                        value = (null != dataSet) ? dataSet.Locale : _culture;
                    }
                    if (_culture != value && !_culture.Equals(value)) {
                        bool flag = false;
                        bool exceptionThrown = false;
                        CultureInfo oldLocale = _culture;
                        bool oldUserSet = _cultureUserSet;
                        try {
                            _cultureUserSet = true;
                            SetLocaleValue(value, true, false);
                            if ((null == DataSet) || DataSet.ValidateLocaleConstraint()) {
                                flag = false;
                                SetLocaleValue(value, true, true);
                                flag = true;
                            }
                        }
                        catch {
                            exceptionThrown = true;
                            throw;
                        }
                        finally {
                            if (!flag) { // reset old locale if ValidationFailed or exception thrown
                                try {
                                    SetLocaleValue(oldLocale, true, true);
                                }
                                catch(Exception e) { // failed to reset all indexes for all constraints
                                    if (!Common.ADP.IsCatchableExceptionType(e)) {
                                        throw;
                                    }
                                    Common.ADP.TraceExceptionWithoutRethrow(e);
                                }
                                _cultureUserSet = oldUserSet;
                                if (!exceptionThrown) {
                                    throw ExceptionBuilder.CannotChangeCaseLocale(null);
                                }
                            }
                        }
                        SetLocaleValue(value, true, true);
                    }
                    _cultureUserSet = userSet;
                }
                finally{
                    Bid.ScopeLeave(ref hscp);
                }
            }
        }
 
        internal bool SetLocaleValue(CultureInfo culture, bool userSet, bool resetIndexes) {
            Debug.Assert(null != culture, "SetLocaleValue: no locale");
            if (userSet || resetIndexes || (!_cultureUserSet && !_culture.Equals(culture))) {
                _culture = culture;
                _compareInfo = null;
                _formatProvider = null;
                _hashCodeProvider = null;
 
                foreach(DataColumn column in Columns) {
                    column._hashCode = GetSpecialHashCode(column.ColumnName);
                }
                if (resetIndexes) {
                    ResetIndexes();
                    foreach (Constraint constraint in Constraints) {
                        constraint.CheckConstraint();
                    }
                }
                return true;
            }
            return false;
        }
 
        internal bool ShouldSerializeLocale() {
            // this method is used design-time scenarios via reflection
            //   by the property grid to show the Locale property in bold or not
            //   by the code dom for persisting the Locale property or not
 
            // we always want the locale persisted if set by user or different the current thread if standalone table
            // but that logic should by performed by the serializion code
            return _cultureUserSet;
        }
 
        /// <devdoc>
        ///    <para>Gets or sets the initial starting size for this table.</para>
        /// </devdoc>
        [
        DefaultValue(50),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableMinimumCapacityDescr)
        ]
        public int MinimumCapacity {
            get {
                return recordManager.MinimumCapacity;
            }
            set {
                if (value != recordManager.MinimumCapacity) {
                    recordManager.MinimumCapacity = value;
                }
            }
        }
 
        internal int RecordCapacity {
            get {
                return recordManager.RecordCapacity;
            }
        }
 
 
        internal int ElementColumnCount {
            get {
                return elementColumnCount;
            }
            set {
                if ((value > 0) && (xmlText != null))
                    throw ExceptionBuilder.TableCannotAddToSimpleContent();
                else elementColumnCount = value;
            }
        }
 
        /// <devdoc>
        /// <para>Gets the collection of parent relations for this <see cref='System.Data.DataTable'/>.</para>
        /// </devdoc>
        [
        Browsable(false),
        ResDescriptionAttribute(Res.DataTableParentRelationsDescr),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
        ]
        public DataRelationCollection ParentRelations {
            get {
                if (parentRelationsCollection == null)
                    parentRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, true);
                return parentRelationsCollection;
            }
        }
 
        internal bool MergingData {
            get {
                return mergingData;
            }
            set {
                mergingData = value;
            }
        }
 
        internal DataRelation[] NestedParentRelations {
            get {
#if DEBUG
                DataRelation[] nRel = FindNestedParentRelations();
                Debug.Assert(nRel.Length == _nestedParentRelations.Length, "nestedParent cache is broken");
                for(int i = 0; i < nRel.Length; i++) {
                    Debug.Assert(null != nRel[i], "null relation");
                    Debug.Assert(null != _nestedParentRelations[i], "null relation");
                    Debug.Assert(nRel[i] == _nestedParentRelations[i], "unequal relations");
                }
#endif
                return _nestedParentRelations;
            }
        }
 
        internal bool SchemaLoading {
            get {
                return schemaLoading;
            }
        }
 
 
        internal void CacheNestedParent() {
            _nestedParentRelations = FindNestedParentRelations();
        }
 
        private DataRelation[] FindNestedParentRelations() {
            List<DataRelation> nestedParents = null;
            foreach(DataRelation relation in this.ParentRelations) {
                if(relation.Nested) {
                    if (null == nestedParents) {
                        nestedParents = new List<DataRelation>();
                    }
                    nestedParents.Add(relation);
                }
            }
            if ((null == nestedParents) || (nestedParents.Count == 0)) {
                return EmptyArrayDataRelation;
            }
            return nestedParents.ToArray();
        }
 
 
        internal int NestedParentsCount {
            get {
                int count = 0;
                foreach(DataRelation relation in this.ParentRelations) {
                    if(relation.Nested)
                        count++;
                }
                return count;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets or sets an array of columns that function as primary keys for the data
        ///       table.</para>
        /// </devdoc>
        [
        TypeConverter(typeof(PrimaryKeyTypeConverter)),
        ResDescriptionAttribute(Res.DataTablePrimaryKeyDescr),
        ResCategoryAttribute(Res.DataCategory_Data),
        Editor("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing)
        ]
        public DataColumn[] PrimaryKey {
            get {
                UniqueConstraint primayKeyConstraint = primaryKey;
                if (null != primayKeyConstraint) {
                    Debug.Assert(2 <= primaryKey.ConstraintIndex.RefCount, "bad primaryKey index RefCount");
                    return primayKeyConstraint.Key.ToArray();
                }
                return zeroColumns;
            }
            set {
                UniqueConstraint key = null;
                UniqueConstraint existingKey = null;
 
                // Loading with persisted property
                if (fInitInProgress && value != null) {
                    delayedSetPrimaryKey = value;
                    return;
                }
 
                if ((value != null) && (value.Length != 0)) {
                    int count = 0;
                    for (int i = 0; i < value.Length; i++) {
                        if (value[i] != null)
                            count++;
                        else
                            break;
                    }
 
                    if (count != 0) {
                        DataColumn[] newValue = value;
                        if (count != value.Length) {
                            newValue = new DataColumn[count];
                            for (int i = 0; i < count; i++)
                                newValue[i] = value[i];
                        }
                        key = new UniqueConstraint(newValue);
                        if (key.Table != this)
                            throw ExceptionBuilder.TableForeignPrimaryKey();
                    }
                }
 
                if (key == primaryKey || (key != null && key.Equals(primaryKey)))
                    return;
 
                // Use an existing UniqueConstraint that matches if one exists
                if ((existingKey = (UniqueConstraint)Constraints.FindConstraint(key)) != null) {
                    key.ColumnsReference.CopyTo(existingKey.Key.ColumnsReference, 0);
                    key = existingKey;
                }
 
                UniqueConstraint oldKey = primaryKey;
                primaryKey = null;
                if (oldKey != null) {
                    oldKey.ConstraintIndex.RemoveRef();
 
                    // SQLBU 429176: if PrimaryKey is removed, reset LoadDataRow indexes
                    if (null != loadIndex) {
                        loadIndex.RemoveRef();
                        loadIndex = null;
                    }
                    if (null != loadIndexwithOriginalAdded) {
                        loadIndexwithOriginalAdded.RemoveRef();
                        loadIndexwithOriginalAdded = null;
                    }
                    if (null != loadIndexwithCurrentDeleted) {
                        loadIndexwithCurrentDeleted.RemoveRef();
                        loadIndexwithCurrentDeleted = null;
                    }
                    Constraints.Remove(oldKey);
                }
 
                // Add the key if there isnt an existing matching key in collection
                if (key != null && existingKey == null)
                    Constraints.Add(key);
 
                primaryKey = key;
 
                Debug.Assert(Constraints.FindConstraint(primaryKey) == primaryKey, "PrimaryKey is not in ConstraintCollection");
                _primaryIndex = (key != null) ? key.Key.GetIndexDesc() : zeroIndexField;
 
                if (primaryKey != null) {
                    // must set index for DataView.Sort before setting AllowDBNull which can fail
                    key.ConstraintIndex.AddRef();
 
                    for (int i = 0; i < key.ColumnsReference.Length; i++)
                        key.ColumnsReference[i].AllowDBNull = false;
                }
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Indicates whether the <see cref='System.Data.DataTable.PrimaryKey'/> property should be persisted.
        ///    </para>
        /// </devdoc>
        private bool ShouldSerializePrimaryKey() {
            return(primaryKey != null);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Resets the <see cref='System.Data.DataTable.PrimaryKey'/> property to its default state.
        ///    </para>
        /// </devdoc>
        private void ResetPrimaryKey() {
            PrimaryKey = null;
        }
 
        /// <devdoc>
        ///    <para>Gets the collection of rows that belong to this table.</para>
        /// </devdoc>
        [Browsable(false), ResDescriptionAttribute(Res.DataTableRowsDescr)]
        public DataRowCollection Rows {
            get {
                return rowCollection;
            }
        }
 
        /// <devdoc>
        ///    <para>Gets or sets the name of the table.</para>
        /// </devdoc>
        [
        RefreshProperties(RefreshProperties.All),
        DefaultValue(""),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableTableNameDescr)
        ]
        public string TableName {
            get {
                return tableName;
            }
            set {
                IntPtr hscp;
                Bid.ScopeEnter(out hscp, "<ds.DataTable.set_TableName|API> %d#, value='%ls'\n", ObjectID, value);
                try {
                    if (value == null) {
                        value = "";
                    }
                    CultureInfo currentLocale = this.Locale;
                    if (String.Compare(tableName, value, true, currentLocale) != 0) {
                        if (dataSet != null) {
                            if (value.Length == 0)
                                throw ExceptionBuilder.NoTableName();
                            if ((0 == String.Compare(value, dataSet.DataSetName, true, dataSet.Locale)) && !fNestedInDataset)
                               throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName);
 
                            DataRelation[] nestedRelations = NestedParentRelations;
                            if (nestedRelations.Length == 0) {
                                dataSet.Tables.RegisterName(value, this.Namespace);
                            }
                            else {
                                foreach(DataRelation rel in nestedRelations) {
                                    if (!rel.ParentTable.Columns.CanRegisterName(value)) {
                                        throw ExceptionBuilder.CannotAddDuplicate2(value);
                                    }
                                }
                                // if it cannot register the following line will throw exception
                                dataSet.Tables.RegisterName(value, this.Namespace);
 
                                foreach(DataRelation rel in nestedRelations) {
                                    rel.ParentTable.Columns.RegisterColumnName(value, null);
                                    rel.ParentTable.Columns.UnregisterName(this.TableName);
                                }
                            }
 
                            if (tableName.Length != 0) {
                                dataSet.Tables.UnregisterName(tableName);
                            }
                        }
                        RaisePropertyChanging("TableName");
                        tableName = value;
                        encodedTableName = null;
                    }
                    else if (String.Compare(tableName, value, false, currentLocale) != 0) {
                        RaisePropertyChanging("TableName");
                        tableName = value;
                        encodedTableName = null;
                    }
                }
                finally {
                    Bid.ScopeLeave(ref hscp);
                }
            }
        }
 
 
        internal string EncodedTableName {
            get {
                string encodedTblName = this.encodedTableName;
                if (null == encodedTblName) {
                    encodedTblName = XmlConvert.EncodeLocalName( this.TableName );
                    this.encodedTableName = encodedTblName;
                }
                return encodedTblName;
            }
        }
        private string GetInheritedNamespace(List<DataTable> visitedTables){
            // if there is nested relation: ie: this table is nested child of a another table and
            // if it is not self nested, return parent tables NS: Meanwhile make sure SQLBUDT 240219 is FIXED
            DataRelation[] nestedRelations = NestedParentRelations;
            if (nestedRelations.Length > 0) {
                for(int i =0; i < nestedRelations.Length; i++) {
                    DataRelation rel = nestedRelations[i];
                    if (rel.ParentTable.tableNamespace != null) {
                        return rel.ParentTable.tableNamespace; // if parent table has a non-null NS, return it
                    }
                }
                // Assumption, in hierarchy of multiple nested relation, a child table with no NS, has DataRelation
                // only and only with parent DataTable witin the same namespace
                int j = 0;
                while(j < nestedRelations.Length &&((nestedRelations[j].ParentTable == this)||(visitedTables.Contains(nestedRelations[j].ParentTable)))) {
                    j++;
                }
                if (j < nestedRelations.Length) {
                    DataTable parentTable = nestedRelations[j].ParentTable;
                    if (!visitedTables.Contains(parentTable))
                        visitedTables.Add(parentTable);
                        return parentTable.GetInheritedNamespace(visitedTables);// this is the same as return parentTable.Namespace
                }
            } // dont put else
            if (DataSet != null) { // if it cant return from parent tables, return NS from dataset, if exists
                return  DataSet.Namespace;
            }
            else {
                return string.Empty;
            }
 
        }
 
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the namespace for the <see cref='System.Data.DataTable'/>.
        ///    </para>
        /// </devdoc>
        [
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableNamespaceDescr)
        ]
        public string Namespace {
            get {
                if (tableNamespace == null) {
                    return GetInheritedNamespace(new List<DataTable>());
                }
                return tableNamespace;
            }
            set {
                IntPtr hscp;
                Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Namespace|API> %d#, value='%ls'\n", ObjectID, value);
                try {
                    if(value != tableNamespace) {
                        if (dataSet != null) {
                            string realNamespace = (value == null ? GetInheritedNamespace(new List<DataTable>()) : value);
                            if (realNamespace != Namespace) {
                                // do this extra check only if the namespace is really going to change
                                // inheritance-wise.
                                if (dataSet.Tables.Contains( this.TableName, realNamespace, true, true))
                                    throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
 
                                CheckCascadingNamespaceConflict(realNamespace);
                            }
                        }
                        CheckNamespaceValidityForNestedRelations(value);
                        DoRaiseNamespaceChange();
                    }
                    tableNamespace = value;
                }
                finally{
                    Bid.ScopeLeave(ref hscp);
                }
            }
        }
        internal bool IsNamespaceInherited() {
            return (null == tableNamespace);
        }
 
        internal void CheckCascadingNamespaceConflict(string realNamespace){
            foreach (DataRelation rel in ChildRelations)
                if ((rel.Nested) && (rel.ChildTable != this) && (rel.ChildTable.tableNamespace == null)) {
                    DataTable childTable = rel.ChildTable;
                    if (dataSet.Tables.Contains( childTable.TableName, realNamespace, false, true))
                        throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
 
                    childTable.CheckCascadingNamespaceConflict(realNamespace);
                }
 
        }
 
        internal void CheckNamespaceValidityForNestedRelations(string realNamespace){
            foreach(DataRelation rel in ChildRelations) {
                if (rel.Nested) {
                    if (realNamespace != null) {
                        rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(realNamespace, this);
                    }
                    else{
                        rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
                    }
                }
            }
            if (realNamespace == null) { // this will affect this table if it has parent relations
                this.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
            }
 
        }
        internal void CheckNamespaceValidityForNestedParentRelations(string ns, DataTable parentTable) {
            foreach(DataRelation rel in ParentRelations){
                if (rel.Nested) {
                    if (rel.ParentTable != parentTable && rel.ParentTable.Namespace != ns) {
                        throw ExceptionBuilder.InValidNestedRelation(this.TableName);
                    }
                }
            }
 
        }
 
        internal void DoRaiseNamespaceChange(){
            RaisePropertyChanging("Namespace");
            // raise column Namespace change
 
            foreach (DataColumn col in Columns)
                if (col._columnUri == null)
                    col.RaisePropertyChanging("Namespace");
 
            foreach (DataRelation rel in ChildRelations)
                if ((rel.Nested) && (rel.ChildTable != this)) {
                    DataTable childTable = rel.ChildTable;
 
                    rel.ChildTable.DoRaiseNamespaceChange();
                }
        }
        /// <devdoc>
        ///    <para>
        ///       Indicates whether the <see cref='System.Data.DataTable.Namespace'/> property should be persisted.
        ///    </para>
        /// </devdoc>
        private bool ShouldSerializeNamespace() {
            return(tableNamespace != null);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Resets the <see cref='System.Data.DataTable.Namespace'/> property to its default state.
        ///    </para>
        /// </devdoc>
        private void ResetNamespace() {
            this.Namespace = null;
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        virtual public void BeginInit() {
            fInitInProgress = true;
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        virtual public void EndInit() {
            if (dataSet == null || !dataSet.fInitInProgress) {
                Columns.FinishInitCollection();
                Constraints.FinishInitConstraints();
                foreach(DataColumn dc in Columns){
                    if (dc.Computed) {
                        dc.Expression = dc.Expression;
                    }
                }
            }
            fInitInProgress = false; // Microsoft : 77890. It is must that we set off this flag after calling FinishInitxxx();
            if (delayedSetPrimaryKey != null) {
                PrimaryKey = delayedSetPrimaryKey;
                delayedSetPrimaryKey = null;
            }
            if (delayedViews.Count > 0) {
                foreach(DataView dv in delayedViews) {
                    dv.EndInit();
                }
                delayedViews.Clear();
            }
            OnInitialized();
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [
        DefaultValue(""),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTablePrefixDescr)
        ]
        public string Prefix {
            get { return tablePrefix;}
            set {
                if (value == null) {
                    value = "";
                }
                Bid.Trace("<ds.DataTable.set_Prefix|API> %d#, value='%ls'\n", ObjectID, value);
                if ((XmlConvert.DecodeName(value) == value) &&
                    (XmlConvert.EncodeName(value) != value))
                    throw ExceptionBuilder.InvalidPrefix(value);
 
 
                tablePrefix = value;
            }
        }
 
        internal DataColumn XmlText {
            get {
                return xmlText;
            }
            set {
                if (xmlText != value) {
                    if (xmlText != null) {
                        if (value != null) {
                            throw ExceptionBuilder.MultipleTextOnlyColumns();
                        }
                        Columns.Remove(xmlText);
                    }
                    else {
                        Debug.Assert(value != null, "Value shoud not be null ??");
                        Debug.Assert(value.ColumnMapping == MappingType.SimpleContent, "should be text node here");
                        if (value != Columns[value.ColumnName])
                            Columns.Add(value);
                    }
                    xmlText = value;
                }
            }
        }
 
        internal decimal MaxOccurs {
            get {
                return maxOccurs;
            }
            set {
                maxOccurs = value;
            }
        }
 
        internal decimal MinOccurs {
            get {
                return minOccurs;
            }
            set {
                minOccurs = value;
            }
        }
 
        internal void SetKeyValues(DataKey key, object[] keyValues, int record) {
            for (int i = 0; i < keyValues.Length; i++) {
                key.ColumnsReference[i][record] = keyValues[i];
            }
        }
 
        internal DataRow FindByIndex(Index ndx, object[] key) {
            Range range = ndx.FindRecords(key);
            if (range.IsNull) {
                return null;
            }
            return this.recordManager[ndx.GetRecord(range.Min)];
        }
 
        internal DataRow FindMergeTarget(DataRow row, DataKey key, Index ndx) {
            DataRow targetRow = null;
 
            // Primary key match
            if (key.HasValue) {
                Debug.Assert(ndx != null);
                int   findRecord = (row.oldRecord == -1) ? row.newRecord : row.oldRecord;
                object[] values = key.GetKeyValues(findRecord);
                targetRow = FindByIndex(ndx, values);
            }
            return targetRow;
        }
 
        private void SetMergeRecords(DataRow row, int newRecord, int oldRecord, DataRowAction action) {
            if (newRecord != -1) {
                SetNewRecord(row, newRecord, action, true, true);
                SetOldRecord(row, oldRecord);
            }
            else {
                SetOldRecord(row, oldRecord);
                if (row.newRecord != -1) {
                    Debug.Assert(action == DataRowAction.Delete, "Unexpected SetNewRecord action in merge function.");
                    SetNewRecord(row, newRecord, action, true, true);
                }
            }
        }
 
        internal DataRow MergeRow(DataRow row, DataRow targetRow, bool preserveChanges, Index idxSearch) {
             if (targetRow == null) {
                targetRow = this.NewEmptyRow();
                targetRow.oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
                targetRow.newRecord = targetRow.oldRecord;
                if(row.oldRecord != row.newRecord) {
                    targetRow.newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
                }
                InsertRow(targetRow, -1);
            }
            else {
                // SQLBU 500789: Record Manager corruption during Merge when target row in edit state
                // the newRecord would be freed and overwrite tempRecord (which became the newRecord)
                // this would leave the DataRow referencing a freed record and leaking memory for the now lost record
                int proposedRecord = targetRow.tempRecord; // by saving off the tempRecord, EndEdit won't free newRecord
                targetRow.tempRecord = -1;
                try {
                    DataRowState saveRowState = targetRow.RowState;
                    int saveIdxRecord = (saveRowState == DataRowState.Added) ? targetRow.newRecord : saveIdxRecord = targetRow.oldRecord;
                     int newRecord;
                     int oldRecord;
                    if (targetRow.RowState == DataRowState.Unchanged && row.RowState == DataRowState.Unchanged) {
                        // unchanged row merging with unchanged row
                        oldRecord = targetRow.oldRecord;
                        newRecord = (preserveChanges) ? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
                        oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, targetRow.oldRecord);
                        SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
                    }
                    else if (row.newRecord == -1) {
                        // Incoming row is deleted
                        oldRecord = targetRow.oldRecord;
                        if (preserveChanges) {
                          newRecord = (targetRow.RowState == DataRowState.Unchanged)? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
                        }
                        else
                            newRecord = -1;
                        oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
 
                        // Change index record, need to update index
                        if (saveIdxRecord != ((saveRowState == DataRowState.Added) ? newRecord : oldRecord)) {
                            SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
                            idxSearch.Reset();
                            saveIdxRecord = ((saveRowState == DataRowState.Added) ? newRecord : oldRecord);
                        } else {
                            SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
                        }
                    }
                    else {
                        // incoming row is added, modified or unchanged (targetRow is not unchanged)
                        oldRecord = targetRow.oldRecord;
                        newRecord = targetRow.newRecord;
                        if (targetRow.RowState == DataRowState.Unchanged) {
                            newRecord = recordManager.CopyRecord(this, oldRecord, -1);
                        }
                        oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
 
                        if (!preserveChanges) {
                            newRecord = recordManager.CopyRecord(row.Table, row.newRecord, newRecord);
                        }
                        SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
                    }
 
                    if (saveRowState == DataRowState.Added && targetRow.oldRecord != -1)
                        idxSearch.Reset();
                    Debug.Assert(saveIdxRecord == ((saveRowState == DataRowState.Added) ? targetRow.newRecord : targetRow.oldRecord), "oops, you change index record without noticing it");
                }
                finally {
                    targetRow.tempRecord = proposedRecord;
                }
            }
 
            // Merge all errors
            if (row.HasErrors) {
                if (targetRow.RowError.Length == 0) {
                    targetRow.RowError = row.RowError;
                } else {
                    targetRow.RowError += " ]:[ " + row.RowError;
                }
                DataColumn[] cols = row.GetColumnsInError();
 
                for (int i = 0; i < cols.Length; i++) {
                    DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
                    targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
                }
            }else {
                if (!preserveChanges) {
                    targetRow.ClearErrors();
                }
            }
 
            return targetRow;
        }
 
        /// <devdoc>
        /// <para>Commits all the changes made to this table since the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
        /// </devdoc>
        public void AcceptChanges() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.AcceptChanges|API> %d#\n", ObjectID);
            try {
                DataRow[] oldRows = new DataRow[Rows.Count];
                Rows.CopyTo(oldRows, 0);
 
                // delay updating of indexes until after all
                // AcceptChange calls have been completed
                SuspendIndexEvents();
                try {
                    for (int i = 0; i < oldRows.Length; ++i) {
                        if (oldRows[i].rowID != -1) {
                            oldRows[i].AcceptChanges();
                        }
                    }
                }
                finally {
                    RestoreIndexEvents(false);
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
             }
        }
 
        // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
        [MethodImpl(MethodImplOptions.NoInlining)] 
        protected virtual DataTable CreateInstance() {
            return (DataTable) Activator.CreateInstance(this.GetType(), true);
        }
 
        public virtual DataTable Clone() {
            return Clone(null);
        }
 
        internal DataTable Clone(DataSet cloneDS) {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Clone|INFO> %d#, cloneDS=%d\n", ObjectID, (cloneDS != null) ? cloneDS.ObjectID : 0);
            try {
                DataTable clone = CreateInstance();
                if (clone.Columns.Count > 0) // Microsoft : To clean up all the schema in strong typed dataset.
                    clone.Reset();
                return CloneTo(clone, cloneDS, false);
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
 
        private DataTable IncrementalCloneTo (DataTable sourceTable, DataTable targetTable) {
            foreach(DataColumn dc in sourceTable.Columns) {
                if (targetTable.Columns[dc.ColumnName] == null) {
                    targetTable.Columns.Add(dc.Clone());
                }
            }
 
            return targetTable;
        }
 
        private DataTable CloneHierarchy (DataTable sourceTable, DataSet ds, Hashtable visitedMap) {
            if (visitedMap == null)
                visitedMap = new Hashtable();
            if (visitedMap.Contains(sourceTable))
                return ((DataTable)visitedMap[sourceTable]);
 
 
            DataTable destinationTable = ds.Tables[sourceTable.TableName, sourceTable.Namespace];
 
            if ((destinationTable != null && destinationTable.Columns.Count > 0)) {
                destinationTable = IncrementalCloneTo(sourceTable,destinationTable);
                   // get extra columns from source into destination , increamental read
            }
            else {
                if (destinationTable == null) {
                    destinationTable = new DataTable();
                    // fxcop: new DataTable values for CaseSensitive, Locale, Namespace will come from CloneTo
                    ds.Tables.Add(destinationTable);
                }
                destinationTable = sourceTable.CloneTo(destinationTable, ds, true);
            }
            visitedMap[sourceTable] = destinationTable;
 
 
            // start cloning relation
            foreach( DataRelation r in sourceTable.ChildRelations ) {
                DataTable childTable = CloneHierarchy((DataTable)r.ChildTable, ds, visitedMap);
             }
 
            return destinationTable;
         }
 
 
        private DataTable CloneTo(DataTable clone, DataSet cloneDS, bool skipExpressionColumns) {
// we do clone datatables while we do readxmlschema, so we do not want to clone columnexpressions if we call this from ReadXmlSchema
// it will cause exception to be thrown in cae expression refers to a table that is not in hirerachy or not created yet
            Debug.Assert(clone != null, "The table passed in has to be newly created empty DataTable.");
 
            // set All properties
            clone.tableName = tableName;
 
            clone.tableNamespace = tableNamespace;
            clone.tablePrefix = tablePrefix;
            clone.fNestedInDataset = fNestedInDataset;
 
            clone._culture = _culture;
            clone._cultureUserSet = _cultureUserSet;
            clone._compareInfo = _compareInfo;
            clone._compareFlags = _compareFlags;
            clone._formatProvider = _formatProvider;
            clone._hashCodeProvider = _hashCodeProvider;
            clone._caseSensitive = _caseSensitive;
            clone._caseSensitiveUserSet = _caseSensitiveUserSet;
 
            clone.displayExpression = displayExpression;
            clone.typeName = typeName; //Microsoft
            clone.repeatableElement = repeatableElement; //Microsoft
            clone.MinimumCapacity = MinimumCapacity;
            clone.RemotingFormat = RemotingFormat;
//            clone.SerializeHierarchy = SerializeHierarchy;
 
            // add all columns
            DataColumnCollection clmns = this.Columns;
            for (int i = 0; i < clmns.Count; i++) {
                clone.Columns.Add(clmns[i].Clone());
            }
 
            // add all expressions if Clone is invoked only on DataTable otherwise DataSet.Clone will assign expressions after creating all relationships.
            if (!skipExpressionColumns && cloneDS == null) {
                for (int i = 0; i < clmns.Count; i++) {
                    clone.Columns[clmns[i].ColumnName].Expression = clmns[i].Expression;
                }
            }
 
            // Create PrimaryKey
            DataColumn[] pkey = PrimaryKey;
            if (pkey.Length > 0) {
                DataColumn[] key = new DataColumn[pkey.Length];
                for (int i = 0; i < pkey.Length; i++) {
                    key[i] = clone.Columns[pkey[i].Ordinal];
                }
                clone.PrimaryKey = key;
            }
 
            // now clone all unique constraints
            // Rename first
            for (int j = 0; j < Constraints.Count; j++)  {
                ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
                UniqueConstraint unique = Constraints[j] as UniqueConstraint;
                if (foreign  != null) {
                    if (foreign.Table == foreign.RelatedTable) {
                        ForeignKeyConstraint clonedConstraint = foreign.Clone(clone);
                        Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
                        if (oldConstraint != null) {
                            oldConstraint.ConstraintName = Constraints[j].ConstraintName;
                        }
                    }
                }
                else if (unique != null) {
                    UniqueConstraint clonedConstraint = unique.Clone(clone);
                    Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
                    if (oldConstraint != null) {
                        oldConstraint.ConstraintName = Constraints[j].ConstraintName;
                        foreach (Object key in clonedConstraint.ExtendedProperties.Keys) {
                            oldConstraint.ExtendedProperties[key] = clonedConstraint.ExtendedProperties[key];
                        }
                    }
                }
            }
 
            // then add
            for (int j = 0; j < Constraints.Count; j++)  {
                if (! clone.Constraints.Contains(Constraints[j].ConstraintName, true)) {
                    ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
                    UniqueConstraint unique = Constraints[j] as UniqueConstraint;
                    if (foreign  != null) {
                        if (foreign.Table == foreign.RelatedTable) {
                            ForeignKeyConstraint newforeign = foreign.Clone(clone);
                            if (newforeign != null) { // we cant make sure that we recieve a cloned FKC,since it depends if table and relatedtable be the same
                                clone.Constraints.Add(newforeign);
                            }
                        }
                    }
                    else if (unique != null) {
                        clone.Constraints.Add(unique.Clone(clone));
                    }
                 }
            }
 
            // ...Extended Properties...
 
            if (this.extendedProperties != null) {
                foreach(Object key in this.extendedProperties.Keys) {
                    clone.ExtendedProperties[key]=this.extendedProperties[key];
                }
            }
 
            return clone;
        }
 
 
        public DataTable Copy(){
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Copy|API> %d#\n", ObjectID);
            try {
                DataTable destTable = this.Clone();
 
                foreach (DataRow row in Rows)
                    CopyRow(destTable, row);
 
                return destTable;
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        /// <devdoc>
        ///    <para>Occurs when a value has been submitted for this column.</para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangingDescr)]
        public event DataColumnChangeEventHandler ColumnChanging {
            add {
                Bid.Trace("<ds.DataTable.add_ColumnChanging|API> %d#\n", ObjectID);
                onColumnChangingDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_ColumnChanging|API> %d#\n", ObjectID);
                onColumnChangingDelegate -= value;
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangedDescr)]
        public event DataColumnChangeEventHandler ColumnChanged {
            add  {
                Bid.Trace("<ds.DataTable.add_ColumnChanged|API> %d#\n", ObjectID);
                onColumnChangedDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_ColumnChanged|API> %d#\n", ObjectID);
                onColumnChangedDelegate -= value;
            }
        }
 
        [
            ResCategoryAttribute(Res.DataCategory_Action),
            ResDescriptionAttribute(Res.DataSetInitializedDescr)
        ]
        public event System.EventHandler  Initialized {
            add {
                onInitialized += value;
            }
            remove {
                onInitialized -= value;
            }
        }
 
        internal event PropertyChangedEventHandler PropertyChanging {
            add {
                Bid.Trace("<ds.DataTable.add_PropertyChanging|INFO> %d#\n", ObjectID);
                onPropertyChangingDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_PropertyChanging|INFO> %d#\n", ObjectID);
                onPropertyChangingDelegate -= value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Occurs after a row in the table has been successfully edited.
        ///    </para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangedDescr)]
        public event DataRowChangeEventHandler RowChanged {
            add {
                Bid.Trace("<ds.DataTable.add_RowChanged|API> %d#\n", ObjectID);
                onRowChangedDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_RowChanged|API> %d#\n", ObjectID);
                onRowChangedDelegate -= value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Occurs when the <see cref='System.Data.DataRow'/> is changing.
        ///    </para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangingDescr)]
        public event DataRowChangeEventHandler RowChanging {
            add {
                Bid.Trace("<ds.DataTable.add_RowChanging|API> %d#\n", ObjectID);
                onRowChangingDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_RowChanging|API> %d#\n", ObjectID);
                onRowChangingDelegate -= value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Occurs before a row in the table is
        ///       about to be deleted.
        ///    </para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletingDescr)]
        public event DataRowChangeEventHandler RowDeleting {
            add {
                Bid.Trace("<ds.DataTable.add_RowDeleting|API> %d#\n", ObjectID);
                onRowDeletingDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_RowDeleting|API> %d#\n", ObjectID);
                onRowDeletingDelegate -= value;
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Occurs after a row in the
        ///       table has been deleted.
        ///    </para>
        /// </devdoc>
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletedDescr)]
        public event DataRowChangeEventHandler RowDeleted {
            add {
                Bid.Trace("<ds.DataTable.add_RowDeleted|API> %d#\n", ObjectID);
                onRowDeletedDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_RowDeleted|API> %d#\n", ObjectID);
                onRowDeletedDelegate -= value;
            }
        }
 
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearingDescr)]
        public event DataTableClearEventHandler TableClearing {
            add {
                Bid.Trace("<ds.DataTable.add_TableClearing|API> %d#\n", ObjectID);
                onTableClearingDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_TableClearing|API> %d#\n", ObjectID);
                onTableClearingDelegate -= value;
            }
        }
 
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearedDescr)]
        public event DataTableClearEventHandler TableCleared {
            add {
                Bid.Trace("<ds.DataTable.add_TableCleared|API> %d#\n", ObjectID);
                onTableClearedDelegate += value;
            }
            remove {
                Bid.Trace("<ds.DataTable.remove_TableCleared|API> %d#\n", ObjectID);
                onTableClearedDelegate -= value;
            }
        }
 
        [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsNewRowDescr)]
        public event DataTableNewRowEventHandler TableNewRow {
            add {
                onTableNewRowDelegate += value;
            }
            remove {
                onTableNewRowDelegate -= value;
            }
        }
 
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override ISite Site {
            get {
                return base.Site;
            }
            set {
                ISite oldSite = Site;
                if (value == null && oldSite != null) {
                    IContainer cont = oldSite.Container;
 
                    if (cont != null) {
                        for (int i = 0; i < Columns.Count; i++) {
                            if (Columns[i].Site != null) {
                                cont.Remove(Columns[i]);
                            }
                        }
                    }
                }
                base.Site = value;
            }
        }
 
        internal DataRow AddRecords(int oldRecord, int newRecord) {
            DataRow row;
            if (oldRecord == -1 && newRecord == -1)
            {
                row = NewRow(-1);
                AddRow(row);
            }
            else
            {
                row = NewEmptyRow();
                row.oldRecord = oldRecord;
                row.newRecord = newRecord;
                InsertRow(row, -1);
            }
            return row;
        }
 
        internal void AddRow(DataRow row) {
            AddRow(row, -1);
        }
 
        internal void AddRow(DataRow row, int proposedID) {
            InsertRow(row, proposedID, -1);
        }
 
        internal void InsertRow(DataRow row, int proposedID, int pos) {
            InsertRow(row, proposedID, pos, /*fireEvent*/true);
        }
 
        internal void InsertRow(DataRow row, long proposedID, int pos, bool fireEvent) {
            Exception deferredException = null;
 
            if (row == null) {
                throw ExceptionBuilder.ArgumentNull("row");
            }
            if (row.Table != this) {
                throw ExceptionBuilder.RowAlreadyInOtherCollection();
            }
            if (row.rowID != -1) {
                throw ExceptionBuilder.RowAlreadyInTheCollection();
            }
            row.BeginEdit(); // ensure something's there.            
 
            int record = row.tempRecord;
            row.tempRecord = -1;
 
            if (proposedID == -1) {
                proposedID = this.nextRowID;
            }
 
            bool rollbackOnException;
            if (rollbackOnException = (nextRowID <= proposedID)) { // WebData 109005
                nextRowID = checked(proposedID + 1);
            }
 
            try {
                try {
                    row.rowID = proposedID;
                    // this method may cause DataView.OnListChanged in which another row may be added
                    SetNewRecordWorker(row, record, DataRowAction.Add, false, false, pos, fireEvent, out deferredException); // now we do add the row to collection before OnRowChanged (RaiseRowChanged)
                }
                catch {
                    if (rollbackOnException && (nextRowID == proposedID+1)) {
                        nextRowID = proposedID;
                    }
                    row.rowID = -1;
                    row.tempRecord = record;
                    throw;
                }
 
                // since expression evaluation occurred in SetNewRecordWorker, there may have been a problem that
                // was deferred to this point.  If so, throw now since row has already been added.
                if (deferredException != null)
                    throw deferredException;
 
                if (EnforceConstraints && !inLoad ) { // if we are evaluating expression, we need to validate constraints
                    int columnCount = columnCollection.Count;
                    for (int i = 0; i < columnCount; ++i) {
                        DataColumn column = columnCollection[i];
                        if (column.Computed) {
                            column.CheckColumnConstraint(row, DataRowAction.Add);
                        }
                    }
                }
            }
            finally {
                row.ResetLastChangedColumn();// if expression is evaluated while adding, before  return, we want to clear it
            }
        }
 
        internal void CheckNotModifying(DataRow row) {
            if (row.tempRecord != -1) {
                row.EndEdit();
                //throw ExceptionBuilder.ModifyingRow();
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Clears the table of all data.</para>
        /// </devdoc>
 
        public void Clear() {
            Clear(true);
        }
        internal void Clear(bool clearAll) {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Clear|INFO> %d#, clearAll=%d{bool}\n", ObjectID, clearAll);
 
            try {
                Debug.Assert(null == rowDiffId, "wasn't previously cleared");
                rowDiffId = null;
 
                if (dataSet != null)
                    dataSet.OnClearFunctionCalled(this);
                bool shouldFireClearEvents = (this.Rows.Count != 0); // if Rows is already empty, this is noop
 
                DataTableClearEventArgs e = null;
                if (shouldFireClearEvents) {
                    e = new DataTableClearEventArgs (this);
                    OnTableClearing(e);
                }
 
                if (dataSet != null && dataSet.EnforceConstraints) {
 
                    for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
                        ForeignKeyConstraint constraint = constraints.GetForeignKeyConstraint();
                        constraint.CheckCanClearParentTable(this);
                    }
                }
 
                recordManager.Clear(clearAll);
 
                // SQLBU 415729: Serious performance issue when calling Clear()
                // this improves performance by iterating over rows instead of computing by index
                foreach(DataRow row in Rows) {
                    row.oldRecord = -1;
                    row.newRecord = -1;
                    row.tempRecord = -1;
                    row.rowID = -1;
                    row.RBTreeNodeId = 0;
                }
                Rows.ArrayClear();
 
                ResetIndexes();
 
                if (shouldFireClearEvents) {
                    OnTableCleared(e);
                }
 
                // SQLBU 501916 - DataTable internal index is corrupted:'5'
                foreach(DataColumn column in Columns) {
                    EvaluateDependentExpressions(column);
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        internal void CascadeAll(DataRow row, DataRowAction action) {
            if (DataSet != null && DataSet.fEnableCascading) {
                for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
                    constraints.GetForeignKeyConstraint().CheckCascade(row, action);
                }
            }
        }
 
        internal void CommitRow(DataRow row) {
            // Fire Changing event
            DataRowChangeEventArgs drcevent = OnRowChanging(null, row, DataRowAction.Commit);
 
            if (!inDataLoad)
                CascadeAll(row, DataRowAction.Commit);
 
            SetOldRecord(row, row.newRecord);
 
            OnRowChanged(drcevent, row, DataRowAction.Commit);
        }
 
        internal int Compare(string s1, string s2) {
            return Compare(s1, s2, null);
        }
 
        internal int Compare(string s1, string s2, CompareInfo comparer) {
            object obj1 = s1;
            object obj2 = s2;
            if (obj1 == obj2)
                return 0;
            if (obj1 == null)
                return -1;
            if (obj2 == null)
                return 1;
 
            int leng1 = s1.Length;
            int leng2 = s2.Length;
 
            for (; leng1 > 0; leng1--) {
                if (s1[leng1-1] != 0x20 && s1[leng1-1] != 0x3000) // 0x3000 is Ideographic Whitespace
                    break;
            }
            for (; leng2 > 0; leng2--) {
                if (s2[leng2-1] != 0x20 && s2[leng2-1] != 0x3000)
                    break;
            }
 
            return (comparer ?? this.CompareInfo).Compare(s1, 0, leng1, s2, 0, leng2, _compareFlags);
        }
 
        internal int IndexOf(string s1, string s2) {
            return CompareInfo.IndexOf(s1, s2, _compareFlags);
        }
 
        internal bool IsSuffix(string s1, string s2) {
            return CompareInfo.IsSuffix(s1, s2, _compareFlags);
        }
 
        /// <devdoc>
        ///    <para>Computes the given expression on the current rows that pass the filter criteria.</para>
        /// </devdoc>
        public object Compute(string expression, string filter) {
            DataRow[] rows = Select(filter, "", DataViewRowState.CurrentRows);
            DataExpression expr = new DataExpression(this, expression);
            return expr.Evaluate(rows);
        }
 
        bool System.ComponentModel.IListSource.ContainsListCollection {
            get {
                return false;
            }
        }
 
        internal void CopyRow(DataTable table, DataRow row)
        {
            int oldRecord = -1, newRecord = -1;
 
            if (row == null)
                return;
 
            if (row.oldRecord != -1) {
                oldRecord = table.recordManager.ImportRecord(row.Table, row.oldRecord);
            }
            if (row.newRecord != -1) {
                if (row.newRecord != row.oldRecord) {
                    newRecord = table.recordManager.ImportRecord(row.Table, row.newRecord);
                }
                else
                    newRecord = oldRecord;
            }
 
            DataRow targetRow = table.AddRecords(oldRecord, newRecord);
 
            if (row.HasErrors) {
                targetRow.RowError = row.RowError;
 
                DataColumn[] cols = row.GetColumnsInError();
 
                for (int i = 0; i < cols.Length; i++) {
                    DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
                    targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
                }
            }
 
       }
 
 
        internal void DeleteRow(DataRow row) {
            if (row.newRecord == -1) {
                throw ExceptionBuilder.RowAlreadyDeleted();
            }
 
            // Store.PrepareForDelete(row);
            SetNewRecord(row, -1, DataRowAction.Delete, false, true);
        }
 
        private void CheckPrimaryKey() {
            if (primaryKey == null) throw ExceptionBuilder.TableMissingPrimaryKey();
        }
 
        internal DataRow FindByPrimaryKey(object[] values) {
            CheckPrimaryKey();
            return FindRow(primaryKey.Key, values);
        }
 
        internal DataRow FindByPrimaryKey(object value) {
            CheckPrimaryKey();
            return FindRow(primaryKey.Key, value);
        }
 
        private DataRow FindRow(DataKey key, object[] values) {
            Index index = GetIndex(NewIndexDesc(key));
            Range range = index.FindRecords(values);
            if (range.IsNull)
                return null;
            return recordManager[index.GetRecord(range.Min)];
        }
 
        private DataRow FindRow(DataKey key, object value) {
            Index index = GetIndex(NewIndexDesc(key));
            Range range = index.FindRecords(value);
            if (range.IsNull)
                return null;
            return recordManager[index.GetRecord(range.Min)];
        }
 
        internal string FormatSortString(IndexField[] indexDesc) {
            StringBuilder builder = new StringBuilder();
            foreach (IndexField field in indexDesc) {
                if (0 < builder.Length) {
                    builder.Append(", ");
                }
                builder.Append(field.Column.ColumnName);
                if (field.IsDescending) {
                    builder.Append(" DESC");
                }
            }
            return builder.ToString();
        }
 
        internal void FreeRecord(ref int record) {
            recordManager.FreeRecord(ref record);
        }
 
        public DataTable GetChanges() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#\n", ObjectID);
            try {
                DataTable dtChanges = this.Clone();
                DataRow row = null;
 
                for (int i = 0; i < Rows.Count; i++) {
                    row = Rows[i];
                    if (row.oldRecord != row.newRecord)
                        dtChanges.ImportRow(row);
                }
 
                if (dtChanges.Rows.Count == 0)
                    return null;
 
                return dtChanges;
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        public DataTable GetChanges(DataRowState rowStates)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#, rowStates=%d{ds.DataRowState}\n", ObjectID, (int)rowStates);
            try {
                DataTable dtChanges = this.Clone();
                DataRow row = null;
 
                // check that rowStates is valid DataRowState
                Debug.Assert(Enum.GetUnderlyingType(typeof(DataRowState)) == typeof(Int32), "Invalid DataRowState type");
 
                for (int i = 0; i < Rows.Count; i++) {
                    row = Rows[i];
                    if ((row.RowState & rowStates) != 0)
                        dtChanges.ImportRow(row);
                }
 
                if (dtChanges.Rows.Count == 0)
                    return null;
 
                return dtChanges;
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        /// <devdoc>
        /// <para>Returns an array of <see cref='System.Data.DataRow'/> objects that contain errors.</para>
        /// </devdoc>
        public DataRow[] GetErrors() {
            List<DataRow> errorList = new List<DataRow>();
 
            for (int i = 0; i < Rows.Count; i++) {
                DataRow row = Rows[i];
                if (row.HasErrors) {
                    errorList.Add(row);
                }
            }
            DataRow[] temp = NewRowArray(errorList.Count);
            errorList.CopyTo(temp);
            return temp;
        }
 
        internal Index GetIndex(IndexField[] indexDesc) {
            return GetIndex(indexDesc, DataViewRowState.CurrentRows, (IFilter)null);
        }
 
        internal Index GetIndex(string sort, DataViewRowState recordStates, IFilter rowFilter) {
            return GetIndex(ParseSortString(sort), recordStates, rowFilter);
        }
 
        internal Index GetIndex(IndexField[] indexDesc, DataViewRowState recordStates, IFilter rowFilter) {
            indexesLock.AcquireReaderLock(-1);
            try {
                for (int i = 0; i < indexes.Count; i++) {
                    Index index = indexes[i];
                    if (index != null) {
                        if (index.Equal(indexDesc, recordStates, rowFilter)) {
                            return index;
                        }
                    }
                }
            }
            finally {
                indexesLock.ReleaseReaderLock();
            }
            Index ndx = new Index(this, indexDesc, recordStates, rowFilter);
            ndx.AddRef();
            return ndx;
        }
 
        IList System.ComponentModel.IListSource.GetList() {
            return DefaultView;
        }
 
 
        internal List<DataViewListener> GetListeners() {
            return _dataViewListeners;
        }
 
        // We need a HashCodeProvider for Case, Kana and Width insensitive
        internal int GetSpecialHashCode(string name) {
            int i;
            for (i = 0; (i < name.Length) && (0x3000 > name[i]); ++i);
 
            if (name.Length == i) {
                if (null == _hashCodeProvider) {
                    // it should use the CaseSensitive property, but V1 shipped this way
                    _hashCodeProvider = StringComparer.Create(Locale, true);
                }
                return _hashCodeProvider.GetHashCode(name);
            }
            else {
                return 0;
            }
        }
 
        public void ImportRow(DataRow row)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.ImportRow|API> %d#\n", ObjectID);
            try {
                int oldRecord = -1, newRecord = -1;
 
                if (row == null)
                    return;
 
                if (row.oldRecord != -1) {
                    oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
                }
                if (row.newRecord != -1) {  // row not deleted
                    if (row.RowState != DataRowState.Unchanged) { // not unchanged, it means Added or modified
                        newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
                    }
                    else
                        newRecord = oldRecord;
                }
 
                if (oldRecord != -1 || newRecord != -1) {
                    DataRow targetRow = AddRecords(oldRecord, newRecord);
 
                    if (row.HasErrors) {
                        targetRow.RowError = row.RowError;
 
                        DataColumn[] cols = row.GetColumnsInError();
 
                        for (int i = 0; i < cols.Length; i++) {
                            DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
                            targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
                        }
                    }
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
 
       }
 
        internal void InsertRow(DataRow row, long proposedID) {
            IntPtr hscp;
 
            Bid.ScopeEnter(out hscp, "<ds.DataTable.InsertRow|INFO> %d#, row=%d\n", ObjectID, row.ObjectID);
            try {
                if (row.Table != this) {
                    throw ExceptionBuilder.RowAlreadyInOtherCollection();
                }
                if (row.rowID != -1) {
                    throw ExceptionBuilder.RowAlreadyInTheCollection();
                }
                if (row.oldRecord == -1 && row.newRecord == -1) {
                    throw ExceptionBuilder.RowEmpty();
                }
 
                if (proposedID == -1)
                    proposedID = nextRowID;
 
                row.rowID = proposedID;
                if (nextRowID <= proposedID)
                    nextRowID = checked(proposedID + 1);
 
                DataRowChangeEventArgs drcevent = null;
 
 
                if (row.newRecord != -1) {
                    row.tempRecord = row.newRecord;
                    row.newRecord = -1;
 
                    try {
                        drcevent = RaiseRowChanging(null, row, DataRowAction.Add, true);
                    }
                    catch {
                        row.tempRecord = -1;
                        throw;
                    }
 
                    row.newRecord = row.tempRecord;
                    row.tempRecord = -1;
                }
 
                if (row.oldRecord != -1)
                    recordManager[row.oldRecord] = row;
 
                if (row.newRecord != -1)
                    recordManager[row.newRecord] = row;
 
                Rows.ArrayAdd(row); // SQL BU Defect Tracking 247738, 323482 row should be in the
                                    // collection when maintaining the indexes                  
 
                if (row.RowState == DataRowState.Unchanged){ //  how about row.oldRecord == row.newRecord both == -1
                    RecordStateChanged(row.oldRecord, DataViewRowState.None, DataViewRowState.Unchanged);
                }
                else {
                    RecordStateChanged(row.oldRecord, DataViewRowState.None, row.GetRecordState(row.oldRecord),
                                       row.newRecord, DataViewRowState.None, row.GetRecordState(row.newRecord));
                }
 
                if (dependentColumns != null && dependentColumns.Count > 0)
                    EvaluateExpressions(row, DataRowAction.Add, null);
 
                RaiseRowChanged(drcevent, row, DataRowAction.Add);
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        private IndexField [] NewIndexDesc(DataKey key) {
            Debug.Assert(key.HasValue);
            IndexField[] indexDesc = key.GetIndexDesc();
            IndexField[] newIndexDesc = new IndexField[indexDesc.Length];
            Array.Copy(indexDesc, 0, newIndexDesc, 0, indexDesc.Length);
            return newIndexDesc;
        }
 
        internal int NewRecord() {
            return NewRecord(-1);
        }
 
        internal int NewUninitializedRecord() {
            return recordManager.NewRecordBase();
        }
 
        internal int NewRecordFromArray(object[] value) {
            int colCount = columnCollection.Count; // Perf: use the readonly columnCollection field directly
            if (colCount < value.Length) {
                throw ExceptionBuilder.ValueArrayLength();
            }
            int record = recordManager.NewRecordBase();
            try {
                for (int i = 0; i < value.Length; i++) {
                    if (null != value[i]) {
                        columnCollection[i][record] = value[i];
                    }
                    else {
                        columnCollection[i].Init(record);  // Increase AutoIncrementCurrent
                    }
                }
                for (int i = value.Length; i < colCount; i++) {
                    columnCollection[i].Init(record);
                }
                return record;
            }
            catch (Exception e) {
                // 
                if (Common.ADP.IsCatchableOrSecurityExceptionType (e)) {
                    FreeRecord(ref record); // WebData 104246
                }
                throw;
            }
        }
 
        internal int NewRecord(int sourceRecord) {
            int record = recordManager.NewRecordBase();
 
            int count = columnCollection.Count;
            if (-1 == sourceRecord) {
                for (int i = 0; i < count; ++i) {
                    columnCollection[i].Init(record);
                }
            }
            else {
                for (int i = 0; i < count; ++i) {
                    columnCollection[i].Copy(sourceRecord, record);
                }
            }
            return record;
        }
 
        internal DataRow NewEmptyRow() {
            rowBuilder._record = -1;
            DataRow dr = NewRowFromBuilder( rowBuilder );
            if (dataSet != null) {
                DataSet.OnDataRowCreated( dr );
            }
            return dr;
        }
 
        private DataRow NewUninitializedRow() {
            DataRow dr = NewRow(NewUninitializedRecord());
            return dr;
        }
 
        /// <devdoc>
        /// <para>Creates a new <see cref='System.Data.DataRow'/>
        /// with the same schema as the table.</para>
        /// </devdoc>
        public DataRow NewRow() {
            DataRow dr = NewRow(-1);
            NewRowCreated(dr); // this is the only API we want this event to be fired
            return dr;
        }
 
        // Only initialize DataRelation mapping columns (approximately hidden columns)
        internal DataRow CreateEmptyRow() {
            DataRow row = this.NewUninitializedRow();
 
            foreach( DataColumn c in this.Columns ) {
                if (!XmlToDatasetMap.IsMappedColumn(c)) {
                    if (!c.AutoIncrement) {
                        if (c.AllowDBNull) {
                            row[c] = DBNull.Value;
                        }
                        else if(c.DefaultValue!=null){
                            row[c] = c.DefaultValue;
                        }
                    }
                    else {
                        c.Init(row.tempRecord);
                    }
                }
            }
            return row;
        }
 
        private void NewRowCreated(DataRow row) {
            if (null != onTableNewRowDelegate) {
                DataTableNewRowEventArgs eventArg =  new DataTableNewRowEventArgs(row);
                OnTableNewRow(eventArg);
            }
        }
 
        internal DataRow NewRow(int record) {
            if (-1 == record) {
                record = NewRecord(-1);
            }
 
            rowBuilder._record = record;
            DataRow row = NewRowFromBuilder( rowBuilder );
            recordManager[record] = row;
 
            if (dataSet != null)
                DataSet.OnDataRowCreated( row );
 
            return row;
        }
 
        // This is what a subclassed dataSet overrides to create a new row.
        protected virtual DataRow NewRowFromBuilder(DataRowBuilder builder) {
            return new DataRow(builder);
        }
 
        /// <devdoc>
        ///    <para>Gets the row type.</para>
        /// </devdoc>
        protected virtual Type GetRowType() {
            return typeof(DataRow);
        }
 
        // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
        [MethodImpl(MethodImplOptions.NoInlining)] 
        protected internal DataRow[] NewRowArray(int size) {
            if (IsTypedDataTable) {
                if (0 == size) {
                    if (null == EmptyDataRowArray) {
                        EmptyDataRowArray = (DataRow[]) Array.CreateInstance(GetRowType(), 0);
                    }
                    return EmptyDataRowArray;
                }
                return (DataRow[]) Array.CreateInstance(GetRowType(), size);
            }
            else {
                return ((0 == size) ? DataTable.zeroRows : new DataRow[size]);
            }
        }
 
        internal bool NeedColumnChangeEvents {
            get {
                return (IsTypedDataTable || (null != onColumnChangingDelegate) || (null != onColumnChangedDelegate));
            }
        }
 
        protected internal virtual void OnColumnChanging(DataColumnChangeEventArgs e) {
            // intentionally allow exceptions to bubble up.  We haven't committed anything yet.
            Debug.Assert(e != null, "e should not be null");
            if (onColumnChangingDelegate != null) {
                Bid.Trace("<ds.DataTable.OnColumnChanging|INFO> %d#\n", ObjectID);
                onColumnChangingDelegate(this, e);
            }
        }
 
        protected internal virtual void OnColumnChanged(DataColumnChangeEventArgs e) {
            Debug.Assert(e != null, "e should not be null");
            if (onColumnChangedDelegate != null) {
                Bid.Trace("<ds.DataTable.OnColumnChanged|INFO> %d#\n", ObjectID);
                onColumnChangedDelegate(this, e);
            }
        }
 
        protected virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent) {
            if (onPropertyChangingDelegate != null) {
                Bid.Trace("<ds.DataTable.OnPropertyChanging|INFO> %d#\n", ObjectID);
                onPropertyChangingDelegate(this, pcevent);
            }
        }
 
        internal void OnRemoveColumnInternal(DataColumn column) {
            OnRemoveColumn(column);
        }
 
        /// <devdoc>
        /// <para>Notifies the <see cref='System.Data.DataTable'/> that a <see cref='System.Data.DataColumn'/> is
        ///    being removed.</para>
        /// </devdoc>
        protected virtual void OnRemoveColumn(DataColumn column) {
        }
 
        private DataRowChangeEventArgs OnRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
            if ((null != onRowChangedDelegate) || IsTypedDataTable) {
                if (null == args) {
                    args = new DataRowChangeEventArgs(eRow, eAction);
                }
                OnRowChanged(args);
            }
            return args;
        }
 
        private DataRowChangeEventArgs OnRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
            if ((null != onRowChangingDelegate) || IsTypedDataTable) {
                if (null == args) {
                    args = new DataRowChangeEventArgs(eRow, eAction);
                }
                OnRowChanging(args);
            }
            return args;
        }
 
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Data.DataTable.RowChanged'/> event.
        ///    </para>
        /// </devdoc>
        protected virtual void OnRowChanged(DataRowChangeEventArgs e) {
            Debug.Assert((null != e) && ((null != onRowChangedDelegate) || IsTypedDataTable), "OnRowChanged arguments");
            if (onRowChangedDelegate != null) {
                Bid.Trace("<ds.DataTable.OnRowChanged|INFO> %d#\n", ObjectID);
                onRowChangedDelegate(this, e);
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Data.DataTable.RowChanging'/> event.
        ///    </para>
        /// </devdoc>
        protected virtual void OnRowChanging(DataRowChangeEventArgs e) {
            Debug.Assert((null != e) && ((null != onRowChangingDelegate) || IsTypedDataTable), "OnRowChanging arguments");
            if (onRowChangingDelegate != null) {
                Bid.Trace("<ds.DataTable.OnRowChanging|INFO> %d#\n", ObjectID);
                onRowChangingDelegate(this, e);
           }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Data.DataTable.OnRowDeleting'/> event.
        ///    </para>
        /// </devdoc>
        protected virtual void OnRowDeleting(DataRowChangeEventArgs e) {
            Debug.Assert((null != e) && ((null != onRowDeletingDelegate) || IsTypedDataTable), "OnRowDeleting arguments");
            if (onRowDeletingDelegate != null) {
                Bid.Trace("<ds.DataTable.OnRowDeleting|INFO> %d#\n", ObjectID);
                onRowDeletingDelegate(this, e);
            }
        }
 
        /// <devdoc>
        ///    <para>
        ///       Raises the <see cref='System.Data.DataTable.OnRowDeleted'/> event.
        ///    </para>
        /// </devdoc>
        protected virtual void OnRowDeleted(DataRowChangeEventArgs e) {
            Debug.Assert((null != e) && ((null != onRowDeletedDelegate) || IsTypedDataTable), "OnRowDeleted arguments");
            if (onRowDeletedDelegate != null) {
                Bid.Trace("<ds.DataTable.OnRowDeleted|INFO> %d#\n", ObjectID);
                onRowDeletedDelegate(this, e);
            }
        }
 
        protected virtual void OnTableCleared(DataTableClearEventArgs e) {
            if (onTableClearedDelegate != null) {
                Bid.Trace("<ds.DataTable.OnTableCleared|INFO> %d#\n", ObjectID);
                onTableClearedDelegate(this, e);
            }
        }
 
        protected virtual void OnTableClearing(DataTableClearEventArgs e) {
            if (onTableClearingDelegate != null) {
                Bid.Trace("<ds.DataTable.OnTableClearing|INFO> %d#\n", ObjectID);
                onTableClearingDelegate(this, e);
            }
        }
 
        protected virtual void OnTableNewRow(DataTableNewRowEventArgs  e) {
            if (onTableNewRowDelegate != null) {
                Bid.Trace("<ds.DataTable.OnTableNewRow|INFO> %d#\n", ObjectID);
                onTableNewRowDelegate(this, e);
            }
        }
 
        private void OnInitialized() {
            if (onInitialized != null) {
                Bid.Trace("<ds.DataTable.OnInitialized|INFO> %d#\n", ObjectID);
                onInitialized(this, EventArgs.Empty);
            }
        }
 
 
        internal IndexField[] ParseSortString(string sortString) {
            IndexField[] indexDesc = zeroIndexField;
            if ((null != sortString) && (0 < sortString.Length)) {
                string[] split = sortString.Split(new char[] { ','});
                indexDesc = new IndexField[split.Length];
 
                for (int i = 0; i < split.Length; i++) {
                    string current = split[i].Trim();
 
                    // handle ASC and DESC.
                    int length = current.Length;
                    bool descending = false;
                    if (length >= 5 && String.Compare(current, length - 4, " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0) {
                        current = current.Substring(0, length - 4).Trim();
                    }
                    else if (length >= 6 && String.Compare(current, length - 5, " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0) {
                        descending = true;
                        current = current.Substring(0, length - 5).Trim();
                    }
 
                    // handle brackets.
                    if (current.StartsWith("[", StringComparison.Ordinal)) {
                        if (current.EndsWith("]", StringComparison.Ordinal)) {
                            current = current.Substring(1, current.Length - 2);
                        }
                        else {
                            throw ExceptionBuilder.InvalidSortString(split[i]);
                        }
                    }
 
                    // find the column.
                    DataColumn column = Columns[current];
                    if(column == null) {
                        throw ExceptionBuilder.ColumnOutOfRange(current);
                    }
                    indexDesc[i] = new IndexField(column, descending);
                }
            }
            return indexDesc;
        }
 
        internal void RaisePropertyChanging(string name) {
            OnPropertyChanging(new PropertyChangedEventArgs(name));
        }
 
        // Notify all indexes that record changed.
        // Only called when Error was changed.
        internal void RecordChanged(int record) {
            Debug.Assert (record != -1, "Record number must be given");
            SetShadowIndexes(); // how about new assert?
            try {
                int numIndexes = shadowIndexes.Count;
                for (int i = 0; i < numIndexes; i++) {
                    Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                    if (0 < ndx.RefCount) {
                        ndx.RecordChanged(record);
                    }
                }
            }
            finally{
                RestoreShadowIndexes();
            }
        }
 
// for each index in liveindexes invok RecordChanged
// oldIndex and newIndex keeps  position of record before delete and after insert in each index in order
// LiveIndexes[n-m] will have its information in oldIndex[n-m] and  newIndex[n-m]
        internal void RecordChanged(int[] oldIndex, int[] newIndex) {
            SetShadowIndexes();
            Debug.Assert (oldIndex.Length == newIndex.Length,  "Size oldIndexes and newIndexes should be the same");
            Debug.Assert (oldIndex.Length == shadowIndexes.Count, "Size of OldIndexes should be the same as size of Live indexes");
            try{
                int numIndexes = shadowIndexes.Count;
                for (int i = 0; i < numIndexes; i++) {
                    Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                    if (0 < ndx.RefCount) {
                        ndx.RecordChanged(oldIndex[i], newIndex[i]);
                    }
                }
 
            }
            finally{
                RestoreShadowIndexes();
            }
        }
 
        internal void RecordStateChanged(int record, DataViewRowState oldState, DataViewRowState newState) {
            SetShadowIndexes();
            try{
                int numIndexes = shadowIndexes.Count;
                for (int i = 0; i < numIndexes; i++) {
                    Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                    if (0 < ndx.RefCount) {
                        ndx.RecordStateChanged(record, oldState, newState);
                    }
                }
            }
            finally{
                RestoreShadowIndexes();
            }
            // System.Data.XML.Store.Store.OnROMChanged(record, oldState, newState);
        }
 
 
        internal void RecordStateChanged(int record1, DataViewRowState oldState1, DataViewRowState newState1,
                                         int record2, DataViewRowState oldState2, DataViewRowState newState2) {
            SetShadowIndexes();
            try{
                int numIndexes = shadowIndexes.Count;
                for (int i = 0; i < numIndexes; i++) {
                    Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                    if (0 < ndx.RefCount) {
                        if (record1 != -1 && record2 != -1)
                            ndx.RecordStateChanged(record1, oldState1, newState1,
                                                   record2, oldState2, newState2);
                        else if (record1 != -1)
                            ndx.RecordStateChanged(record1, oldState1, newState1);
                        else if (record2 != -1)
                            ndx.RecordStateChanged(record2, oldState2, newState2);
                    }
                }
            }
            finally {
                RestoreShadowIndexes();
            }
            // System.Data.XML.Store.Store.OnROMChanged(record1, oldState1, newState1, record2, oldState2, newState2);
        }
 
 
// RemoveRecordFromIndexes removes the given record (using row and version) from all indexes and it  stores and returns the position of deleted
// record from each index
// IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
        internal int[] RemoveRecordFromIndexes(DataRow row, DataRowVersion  version) {
            int    indexCount          =  LiveIndexes.Count;
            int [] positionIndexes =  new int[indexCount];
 
            int recordNo = row.GetRecordFromVersion(version);
            DataViewRowState states = row.GetRecordState(recordNo);
 
            while (--indexCount >= 0) {
                if (row.HasVersion(version) && ((states & indexes[indexCount].RecordStates) != DataViewRowState.None)) {
                    int index = indexes[indexCount].GetIndex(recordNo);
                    if (index > -1) {
                        positionIndexes [indexCount] = index;
                        indexes[indexCount].DeleteRecordFromIndex(index); // this will delete the record from index and MUSt not fire event
                    }
                    else {
                        positionIndexes [indexCount] = -1; // this means record was not in index
                    }
                }
                else {
                    positionIndexes [indexCount] = -1; // this means record was not in index
                }
            }
            return positionIndexes;
        }
 
// InsertRecordToIndexes inserts the given record (using row and version) to all indexes and it  stores and returns the position of inserted
// record to each index
// IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
        internal int[] InsertRecordToIndexes(DataRow row, DataRowVersion  version) {
            int    indexCount          =  LiveIndexes.Count;
            int [] positionIndexes =  new int[indexCount];
 
            int recordNo = row.GetRecordFromVersion(version);
            DataViewRowState states = row.GetRecordState(recordNo);
 
            while (--indexCount >= 0) {
                if (row.HasVersion(version)) {
                    if ((states & indexes[indexCount].RecordStates) != DataViewRowState.None) {
                        positionIndexes [indexCount] = indexes[indexCount].InsertRecordToIndex(recordNo);
                    }
                    else {
                        positionIndexes [indexCount] = -1;
                    }
                }
            }
            return positionIndexes;
        }
 
        internal void SilentlySetValue(DataRow dr, DataColumn dc, DataRowVersion version, object newValue) {
            // get record for version
            int record = dr.GetRecordFromVersion(version);
 
            bool equalValues = false;
            if (DataStorage.IsTypeCustomType(dc.DataType) && newValue != dc[record]) {
                // if UDT storage, need to check if reference changed. See bug 385182
                equalValues = false;
            }
            else {
                equalValues = dc.CompareValueTo(record, newValue, true);
            }
 
            // if expression has changed
            if (!equalValues) {
                int[] oldIndex = dr.Table.RemoveRecordFromIndexes(dr, version);// conditional, if it exists it will try to remove with no event fired
                dc.SetValue(record, newValue);
                int[] newIndex = dr.Table.InsertRecordToIndexes(dr, version);// conditional, it will insert if it qualifies, no event will be fired
                if (dr.HasVersion(version)) {
                    if (version != DataRowVersion.Original) {
                        dr.Table.RecordChanged(oldIndex, newIndex);
                    }
                    if (dc.dependentColumns != null) {
                        //BugBug - passing in null for cachedRows.  This means expression columns as keys does not work when key changes.
                        dc.Table.EvaluateDependentExpressions(dc.dependentColumns, dr, version, null);
                    }
                }
             }
             dr.ResetLastChangedColumn();
        }
 
        /// <devdoc>
        ///    <para>Rolls back all changes that have been made to the table
        ///       since it was loaded, or the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
        /// </devdoc>
        public void RejectChanges() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.RejectChanges|API> %d#\n", ObjectID);
            try{
                DataRow[] oldRows = new DataRow[Rows.Count];
                Rows.CopyTo(oldRows, 0);
 
                for (int i = 0; i < oldRows.Length; i++) {
                    RollbackRow(oldRows[i]);
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
 
        }
 
        internal void RemoveRow(DataRow row, bool check) {
            if (row.rowID == -1) {
                throw ExceptionBuilder.RowAlreadyRemoved();
            }
 
            if (check && dataSet != null) {
                for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
                    constraints.GetForeignKeyConstraint().CheckCanRemoveParentRow(row);
                }
            }
 
            int oldRecord = row.oldRecord;
            int newRecord = row.newRecord;
 
            DataViewRowState oldRecordStatePre = row.GetRecordState(oldRecord);
            DataViewRowState newRecordStatePre = row.GetRecordState(newRecord);
 
            row.oldRecord = -1;
            row.newRecord = -1;
 
            if (oldRecord == newRecord) {
                oldRecord = -1;
            }
 
            RecordStateChanged(oldRecord, oldRecordStatePre, DataViewRowState.None,
                               newRecord, newRecordStatePre, DataViewRowState.None);
 
            FreeRecord(ref oldRecord);
            FreeRecord(ref newRecord);
 
            row.rowID = -1;
            Rows.ArrayRemove(row);
        }
 
        // Resets the table back to its original state.
        public virtual void Reset() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Reset|API> %d#\n", ObjectID);
            try {
                Clear();
                ResetConstraints();
 
                DataRelationCollection dr = this.ParentRelations;
                int count = dr.Count;
                  while (count > 0) {
                   count--;
                   dr.RemoveAt(count);
                }
 
                dr = this.ChildRelations;
                count = dr.Count;
                  while (count > 0) {
                   count--;
                   dr.RemoveAt(count);
                }
 
                Columns.Clear();
                indexes.Clear();
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        internal void ResetIndexes() {
            ResetInternalIndexes(null);
        }
 
        internal void ResetInternalIndexes(DataColumn column) {
            Debug.Assert(null != indexes, "unexpected null indexes");
            SetShadowIndexes();
            try{
                // the length of shadowIndexes will not change
                // but the array instance may change during
                // events during Index.Reset
                int numIndexes = shadowIndexes.Count;
                for (int i = 0; i < numIndexes; i++) {
                    Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
                    if (0 < ndx.RefCount) {
                        if (null == column) {
                            ndx.Reset();
                        }
                        else {
                            // SQLBU 501916: DataTable internal index is corrupted:'5'
                            bool found = false;
                            foreach(IndexField field in ndx.IndexFields) {
                                if (Object.ReferenceEquals(column, field.Column)) {
                                    found = true;
                                    break;
                                    
                                }
                            }
                            if (found) {
                                ndx.Reset();
                            }
                        }
                    }
                }
            }
            finally {
                RestoreShadowIndexes();
            }
        }
 
        internal void RollbackRow(DataRow row) {
            row.CancelEdit();
            SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
        }
 
        private DataRowChangeEventArgs RaiseRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
            try {
                if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangedDelegate))) {
                    args = OnRowChanged(args, eRow, eAction);
                }
                // check if we deleting good row
                else if (DataRowAction.Delete == eAction && eRow.newRecord == -1 && (IsTypedDataTable || (null != onRowDeletedDelegate))) {
                    if (null == args) {
                        args = new DataRowChangeEventArgs(eRow, eAction);
                    }
                    OnRowDeleted(args);
                }
            }
            catch (Exception f) {
               // 
               if (!Common.ADP.IsCatchableExceptionType(f)) {
                 throw;
               }
               ExceptionBuilder.TraceExceptionWithoutRethrow(f);
               // ignore the exception
            }
            return args;
        }
 
        private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
            if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangingDelegate))) {
                eRow.inChangingEvent = true;
 
                // don't catch
                try {
                    args = OnRowChanging(args, eRow, eAction);
                }
                finally {
                    eRow.inChangingEvent = false;
                }
            }
            // check if we deleting good row
            else if (DataRowAction.Delete == eAction && eRow.newRecord != -1 && (IsTypedDataTable || (null != onRowDeletingDelegate))) {
                eRow.inDeletingEvent = true;
                // don't catch
                try {
                    if (null == args) {
                        args = new DataRowChangeEventArgs(eRow, eAction);
                    }
                    OnRowDeleting(args);
                }
                finally {
                    eRow.inDeletingEvent = false;
                }
            }
            return args;
        }
 
        private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, bool fireEvent) {
 
            // check all constraints
            if (EnforceConstraints && !inLoad ) {
                int columnCount = columnCollection.Count;
                for(int i = 0; i < columnCount; ++i) {
                    DataColumn column = columnCollection[i];
                    if (!column.Computed || eAction != DataRowAction.Add) {
                        column.CheckColumnConstraint(eRow, eAction);
                    }
                }
 
                int constraintCount = constraintCollection.Count;
                for(int i = 0; i < constraintCount; ++i) {
                    constraintCollection[i].CheckConstraint(eRow, eAction);
                }
            }
 
            // $$anandra.  Check this event out. May be an issue.
            if (fireEvent) {
                args = RaiseRowChanging(args, eRow, eAction);
            }
 
            if (!inDataLoad) {
                // cascade things...
                if (!MergingData && eAction != DataRowAction.Nothing && eAction != DataRowAction.ChangeOriginal) {
                    CascadeAll(eRow, eAction);
                }
            }
            return args;
        }
 
        /// <devdoc>
        /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects.</para>
        /// </devdoc>
        public DataRow[] Select() {
            Bid.Trace("<ds.DataTable.Select|API> %d#\n", ObjectID);
            return new Select(this, "", "", DataViewRowState.CurrentRows).SelectRows();
        }
 
        /// <devdoc>
        /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria in order of
        ///    primary key (or lacking one, order of addition.)</para>
        /// </devdoc>
        public DataRow[] Select(string filterExpression) {
            Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls'\n", ObjectID, filterExpression);
            return new Select(this, filterExpression, "", DataViewRowState.CurrentRows).SelectRows();
        }
 
        /// <devdoc>
        /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria, in the the
        ///    specified sort order.</para>
        /// </devdoc>
        public DataRow[] Select(string filterExpression, string sort) {
            Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls'\n", ObjectID, filterExpression, sort);
            return new Select(this, filterExpression, sort, DataViewRowState.CurrentRows).SelectRows();
        }
 
        /// <devdoc>
        /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter in the order of the
        ///    sort, that match the specified state.</para>
        /// </devdoc>
        public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) {
            Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls', recordStates=%d{ds.DataViewRowState}\n", ObjectID, filterExpression, sort, (int)recordStates);
            return new Select(this, filterExpression, sort, recordStates).SelectRows();
        }
 
        internal void SetNewRecord(DataRow row, int proposedRecord, DataRowAction action = DataRowAction.Change, bool isInMerge = false, bool fireEvent = true, bool suppressEnsurePropertyChanged = false) {
            Exception deferredException = null;
            SetNewRecordWorker(row, proposedRecord, action, isInMerge, suppressEnsurePropertyChanged, -1, fireEvent, out deferredException); // we are going to call below overload from insert
            if (deferredException != null) {
                throw deferredException;
            }
        }
 
        private void SetNewRecordWorker(DataRow row, int proposedRecord, DataRowAction action, bool isInMerge, bool suppressEnsurePropertyChanged,
            int position, bool fireEvent, out Exception deferredException) {
 
            // this is the event workhorse... it will throw the changing/changed events
            // and update the indexes. Used by change, add, delete, revert.
 
            // order of execution is as follows
            //
            // 1) set temp record
            // 2) Check constraints for non-expression columns
            // 3) Raise RowChanging/RowDeleting with temp record
            // 4) set the new record in storage
            // 5) Update indexes with recordStateChanges - this will fire ListChanged & PropertyChanged events on associated views
            // 6) Evaluate all Expressions (exceptions are deferred)- this will fire ListChanged & PropertyChanged events on associated views
            // 7) Raise RowChanged/ RowDeleted
            // 8) Check constraints for expression columns
 
            Debug.Assert(row != null, "Row can't be null.");
            deferredException = null;
            
            if (row.tempRecord != proposedRecord) {
                // $HACK: for performance reasons, EndUpdate calls SetNewRecord with tempRecord == proposedRecord
                if (!inDataLoad) {
                    row.CheckInTable();
                    CheckNotModifying(row);
                }
                if (proposedRecord == row.newRecord) {
                    if (isInMerge) {
                        Debug.Assert(fireEvent, "SetNewRecord is called with wrong parameter");
                        RaiseRowChanged(null, row, action);
                    }
                    return;
                }
 
                Debug.Assert(!row.inChangingEvent, "How can this row be in an infinite loop?");
 
                row.tempRecord = proposedRecord;
            }
            DataRowChangeEventArgs drcevent = null;
 
            try {
                row._action = action;
                drcevent = RaiseRowChanging(null, row, action, fireEvent);
            }
            catch {
                row.tempRecord = -1;
                throw;
            }
            finally {
                row._action = DataRowAction.Nothing;
            }
 
            row.tempRecord = -1;
 
            int currentRecord = row.newRecord;
 
            // if we're deleting, then the oldRecord value will change, so need to track that if it's distinct from the newRecord.
            int secondRecord = (proposedRecord != -1 ?
                                proposedRecord :
                                (row.RowState != DataRowState.Unchanged ?
                                 row.oldRecord :
                                 -1));
 
            if (action == DataRowAction.Add) { //if we come here from insert we do insert the row to collection
                if (position == -1)
                    Rows.ArrayAdd(row);
                else
                    Rows.ArrayInsert(row, position);
            }
 
            List<DataRow> cachedRows = null;
            if ((action == DataRowAction.Delete || action == DataRowAction.Change)
                && dependentColumns != null && dependentColumns.Count > 0) {
                // if there are expression columns, need to cache related rows for deletes and updates (key changes)
                // before indexes are modified.
                cachedRows = new List<DataRow>();
                for (int j = 0; j < ParentRelations.Count; j++) {
                    DataRelation relation = ParentRelations[j];
                    if (relation.ChildTable != row.Table) {
                        continue;
                    }
                    cachedRows.InsertRange(cachedRows.Count, row.GetParentRows(relation));
                }
 
                for (int j = 0; j < ChildRelations.Count; j++) {
                    DataRelation relation = ChildRelations[j];
                    if (relation.ParentTable != row.Table) {
                        continue;
                    }
                    cachedRows.InsertRange(cachedRows.Count, row.GetChildRows(relation));
                }
            }
 
            // Dev10 Bug 688779: DataRowView.PropertyChanged are not raised on RejectChanges
            // if the newRecord is changing, the propertychanged event should be allowed to triggered for ListChangedType.Changed or .Moved
            // unless the specific condition is known that no data has changed, like DataRow.SetModified()
            if (!suppressEnsurePropertyChanged && !row.HasPropertyChanged && (row.newRecord != proposedRecord)
                && (-1 != proposedRecord) // explictly not fixing Dev10 Bug 692044: DataRowView.PropertyChanged are not raised on DataTable.Delete when mixing current and original records in RowStateFilter
                && (-1 != row.newRecord)) // explictly not fixing parts of Dev10 Bug 697909: when mixing current and original records in RowStateFilter
            {
                // DataRow will believe multiple edits occured and
                // DataView.ListChanged event w/ ListChangedType.ItemChanged will raise DataRowView.PropertyChanged event and
                // PropertyChangedEventArgs.PropertyName will now be empty string so
                // WPF will refresh the entire row
                row.LastChangedColumn = null;
                row.LastChangedColumn = null;
            }
 
                // Check whether we need to update indexes
                if (LiveIndexes.Count != 0) {
 
                    // Dev10 bug #463087: DataTable internal index is currupted: '5'
                    if ((-1 == currentRecord) && (-1 != proposedRecord) && (-1 != row.oldRecord) && (proposedRecord != row.oldRecord)) {
                        // the transition from DataRowState.Deleted -> DataRowState.Modified
                        // with same orginal record but new current record
                        // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
                        // for indexes/views listening for both DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent
                        currentRecord = row.oldRecord;
                    }
 
                    DataViewRowState currentRecordStatePre = row.GetRecordState(currentRecord);
                    DataViewRowState secondRecordStatePre = row.GetRecordState(secondRecord);
 
                    row.newRecord = proposedRecord;
                    if (proposedRecord != -1)
                        this.recordManager[proposedRecord] = row;
 
                    DataViewRowState currentRecordStatePost = row.GetRecordState(currentRecord);
                    DataViewRowState secondRecordStatePost = row.GetRecordState(secondRecord);
 
                    // may raise DataView.ListChanged event
                    RecordStateChanged(currentRecord, currentRecordStatePre, currentRecordStatePost,
                        secondRecord, secondRecordStatePre, secondRecordStatePost);
                }
                else {
                    row.newRecord = proposedRecord;
                    if (proposedRecord != -1)
                        this.recordManager[proposedRecord] = row;
                }
 
                // Dev10 Bug 461199 - reset the last changed column here, after all
                // DataViews have raised their DataRowView.PropertyChanged event
                row.ResetLastChangedColumn();
 
                // SQLBU 278737: Record manager corruption when reentrant write operations
                // free the 'currentRecord' only after all the indexes have been updated.
                // Corruption! { if (currentRecord != row.oldRecord) { FreeRecord(ref currentRecord); } }
                // RecordStateChanged raises ListChanged event at which time user may do work
                if (-1 != currentRecord) {
                    if (currentRecord != row.oldRecord)
                    {
                        if ((currentRecord != row.tempRecord) &&   // Delete, AcceptChanges, BeginEdit
                            (currentRecord != row.newRecord) &&    // RejectChanges & SetAdded
                            (row == recordManager[currentRecord])) // AcceptChanges, NewRow
                        {
                            FreeRecord(ref currentRecord);
                        }
                    }
                }
 
            if (row.RowState == DataRowState.Detached && row.rowID != -1) {
                RemoveRow(row, false);
            }
 
            if (dependentColumns != null && dependentColumns.Count > 0) {
                try {
                    EvaluateExpressions(row, action, cachedRows);
                }
                catch (Exception exc) {
                    // For DataRows being added, throwing of exception from expression evaluation is
                    // deferred until after the row has been completely added.
                    if (action != DataRowAction.Add) {
                        throw exc;
                    }
                    else {
                        deferredException = exc;
                    }
                }
            }
 
            try {
                if (fireEvent) {
                    RaiseRowChanged(drcevent, row, action);
                }
            }
            catch (Exception e) {
                // 
                if (!Common.ADP.IsCatchableExceptionType(e)) {
                    throw;
                }
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                // ignore the exception
            }
        }
 
        // this is the event workhorse... it will throw the changing/changed events
        // and update the indexes.
        internal void SetOldRecord(DataRow row, int proposedRecord) {
            if (!inDataLoad) {
                row.CheckInTable();
                CheckNotModifying(row);
            }
 
            if (proposedRecord == row.oldRecord) {
                return;
            }
 
            int originalRecord = row.oldRecord; // cache old record after potential RowChanging event
            try {
                // Check whether we need to update indexes
                if (LiveIndexes.Count != 0) {
 
                    // Dev10 bug #463087: DataTable internal index is currupted: '5'
                    if ((-1 == originalRecord) && (-1 != proposedRecord) && (-1 != row.newRecord) && (proposedRecord != row.newRecord)) {
                        // the transition from DataRowState.Added -> DataRowState.Modified
                        // with same current record but new original record
                        // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
                        // for indexes/views listening for both DataViewRowState.Added | DataViewRowState.ModifiedOriginal
                        originalRecord = row.newRecord;
                    }
 
                    DataViewRowState originalRecordStatePre = row.GetRecordState(originalRecord);
                    DataViewRowState proposedRecordStatePre = row.GetRecordState(proposedRecord);
 
                    row.oldRecord = proposedRecord;
                    if (proposedRecord != -1)
                        this.recordManager[proposedRecord] = row;
 
                    DataViewRowState originalRecordStatePost = row.GetRecordState(originalRecord);
                    DataViewRowState proposedRecordStatePost = row.GetRecordState(proposedRecord);
 
                    RecordStateChanged(originalRecord, originalRecordStatePre, originalRecordStatePost,
                                       proposedRecord, proposedRecordStatePre, proposedRecordStatePost);
                }
                else {
                    row.oldRecord = proposedRecord;
                    if (proposedRecord != -1)
                        this.recordManager[proposedRecord] = row;
                }
            }
            finally {
                if ((originalRecord != -1) && (originalRecord != row.tempRecord) &&
                    (originalRecord != row.oldRecord) && (originalRecord != row.newRecord)) {
 
                    FreeRecord(ref originalRecord);
                }
                // else during an event 'row.AcceptChanges(); row.BeginEdit(); row.EndEdit();'
 
                if (row.RowState == DataRowState.Detached && row.rowID != -1) {
                    RemoveRow(row, false);
                }
            }
        }
 
        private void RestoreShadowIndexes() {
            Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
            shadowCount--;
            if (0 == shadowCount) {
                shadowIndexes = null;
            }
        }
 
        private void SetShadowIndexes() {
            if (null == shadowIndexes) {
                Debug.Assert(0 == shadowCount, "unexpected count");
                shadowIndexes = LiveIndexes;
                shadowCount = 1;
            }
            else {
                Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
                shadowCount++;
            }
        }
 
        internal void ShadowIndexCopy(){
            if (shadowIndexes == indexes) {
                Debug.Assert(0 < indexes.Count, "unexpected");
                shadowIndexes = new List<Index>(indexes);
            }
        }
 
        /// <devdoc>
        /// <para>Returns the <see cref='System.Data.DataTable.TableName'/> and <see cref='System.Data.DataTable.DisplayExpression'/>, if there is one as a concatenated string.</para>
        /// </devdoc>
        public override string ToString() {
            if (this.displayExpression == null)
                return this.TableName;
            else
                return this.TableName + " + " + this.DisplayExpressionInternal;
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void BeginLoadData() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.BeginLoadData|API> %d#\n", ObjectID);
            try {
                if (inDataLoad)
                    return;
 
                inDataLoad = true;
                Debug.Assert(null == loadIndex, "loadIndex should already be null");
                loadIndex  = null;
                // LoadDataRow may have been called before BeginLoadData and already
                // initialized loadIndexwithOriginalAdded & loadIndexwithCurrentDeleted 
 
                initialLoad = (Rows.Count == 0);
                if(initialLoad) {
                    SuspendIndexEvents();
                } else {
                    if (primaryKey != null) {
                        loadIndex = primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
                    }
                    if(loadIndex != null) {
                        loadIndex.AddRef();
                    }
                }
 
                if (DataSet != null) {
                    savedEnforceConstraints = DataSet.EnforceConstraints;
                    DataSet.EnforceConstraints = false;
                }
                else {
                    this.EnforceConstraints = false;
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public void EndLoadData() {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.EndLoadData|API> %d#\n", ObjectID);
            try {
                if (!inDataLoad)
                    return;
 
                if(loadIndex != null) {
                    loadIndex.RemoveRef();
                }
                if (loadIndexwithOriginalAdded  != null) {
                    loadIndexwithOriginalAdded.RemoveRef();
                }
                if (loadIndexwithCurrentDeleted  != null) {
                    loadIndexwithCurrentDeleted.RemoveRef();
                }
 
                loadIndex  = null;
                loadIndexwithOriginalAdded = null;
                loadIndexwithCurrentDeleted = null;
 
                inDataLoad = false;
 
                RestoreIndexEvents(false);
 
                if (DataSet != null)
                    DataSet.EnforceConstraints = savedEnforceConstraints;
                else
                    this.EnforceConstraints = true;
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        /// <devdoc>
        ///    <para>Finds and updates a specific row. If no matching
        ///       row is found, a new row is created using the given values.</para>
        /// </devdoc>
        public DataRow LoadDataRow(object[] values, bool fAcceptChanges) {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, fAcceptChanges=%d{bool}\n", ObjectID, fAcceptChanges);
            try {
                DataRow row;
                if (inDataLoad) {
                    int record = NewRecordFromArray(values);
                    if (loadIndex != null) {
                        // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
                        Debug.Assert(2 <= loadIndex.RefCount, "bad loadIndex.RefCount");
 
                        int result = loadIndex.FindRecord(record);
                        if (result != -1) {
                            int resultRecord = loadIndex.GetRecord(result);
                            row = recordManager[resultRecord];
                            Debug.Assert (row != null, "Row can't be null for index record");
                            row.CancelEdit();
                            if (row.RowState == DataRowState.Deleted)
                                SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
                            SetNewRecord(row, record, DataRowAction.Change, false, true);
                            if (fAcceptChanges)
                                row.AcceptChanges();
                            return row;
                        }
                    }
                    row = NewRow(record);
                    AddRow(row);
                    if (fAcceptChanges)
                        row.AcceptChanges();
                    return row;
                }
                else {
                    // In case, BeginDataLoad is not called yet
                    row = UpdatingAdd(values);
                    if (fAcceptChanges)
                        row.AcceptChanges();
                    return row;
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        /// <devdoc>
        ///    <para>Finds and updates a specific row. If no matching
        ///       row is found, a new row is created using the given values.</para>
        /// </devdoc>
        public DataRow LoadDataRow(object[] values, LoadOption loadOption) {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID,  (int)loadOption);
            try {
                Index indextoUse = null;
                if (this.primaryKey != null) {
                    if (loadOption == LoadOption.Upsert) { // CurrentVersion, and Deleted
                        if (loadIndexwithCurrentDeleted == null) {
                            loadIndexwithCurrentDeleted = this.primaryKey.Key.GetSortIndex(DataViewRowState.CurrentRows |DataViewRowState.Deleted);
                            Debug.Assert(loadIndexwithCurrentDeleted != null, "loadIndexwithCurrentDeleted should not be null" );
                            if (loadIndexwithCurrentDeleted != null) {
                                loadIndexwithCurrentDeleted.AddRef();
                            }
                        }
                        indextoUse = loadIndexwithCurrentDeleted;
                    }
                    else {// CurrentVersion, and Deleted : OverwriteRow, PreserveCurrentValues
                        if (loadIndexwithOriginalAdded == null) {
                            loadIndexwithOriginalAdded  = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows |DataViewRowState.Added);
                            Debug.Assert(loadIndexwithOriginalAdded != null, "loadIndexwithOriginalAdded should not be null");
                            if (loadIndexwithOriginalAdded != null) {
                                loadIndexwithOriginalAdded.AddRef();
                            }
                        }
                        indextoUse = loadIndexwithOriginalAdded;
                    }
                    // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
                    Debug.Assert(2 <= indextoUse.RefCount, "bad indextoUse.RefCount");
                }
                if(inDataLoad && !AreIndexEventsSuspended) { // we do not want to fire any listchanged in new Load/Fill
                    SuspendIndexEvents();// so suspend events here(not suspended == table already has some rows initially)
                }
 
                DataRow dataRow = LoadRow(values, loadOption, indextoUse);// if indextoUse == null, it means we dont have PK,
                                                                          // so LoadRow will take care of just adding the row to end
 
                return dataRow;
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        internal DataRow UpdatingAdd(object[] values) {
            Index index = null;
            if (this.primaryKey != null) {
                index = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
            }
 
            if (index != null) {
                int record = NewRecordFromArray(values);
                int result = index.FindRecord(record);
                if (result != -1) {
                    int resultRecord = index.GetRecord(result);
                    DataRow row = this.recordManager[resultRecord];
                    Debug.Assert (row != null, "Row can't be null for index record");
                    row.RejectChanges();
                    this.SetNewRecord(row, record);
                    return row;
                }
                DataRow row2 = NewRow(record);
                Rows.Add(row2);
                return row2;
            }
 
            return Rows.Add(values);
        }
 
        internal bool UpdatingCurrent(DataRow row, DataRowAction action) {
            return(action == DataRowAction.Add || action == DataRowAction.Change ||
                   action == DataRowAction.Rollback || action == DataRowAction.ChangeOriginal ||
                   action == DataRowAction.ChangeCurrentAndOriginal);
//                (action == DataRowAction.Rollback && row.tempRecord != -1));
}
 
        internal DataColumn AddUniqueKey(int position) {
            if (_colUnique != null)
                return _colUnique;
 
            // check to see if we can use already existant PrimaryKey
            DataColumn[] pkey = PrimaryKey;
            if (pkey.Length == 1)
                // We have one-column primary key, so we can use it in our heirarchical relation
                return pkey[0];
 
            // add Unique, but not primaryKey to the table
 
            string keyName = XMLSchema.GenUniqueColumnName(TableName + "_Id", this);
            DataColumn key = new DataColumn(keyName, typeof(Int32), null, MappingType.Hidden);
            key.Prefix = tablePrefix;
            key.AutoIncrement = true;
            key.AllowDBNull = false;
            key.Unique = true;
 
            if (position == -1)
                Columns.Add(key);
            else { // we do have a problem and Imy idea is it is bug. Ask Enzo while Code review. Why we do not set ordinal when we call AddAt?
                for(int i = Columns.Count -1; i >= position; i--) {
                    this.Columns[i].SetOrdinalInternal(i+1);
                }
                Columns.AddAt(position, key);
                key.SetOrdinalInternal(position);
            }
 
            if (pkey.Length == 0)
                PrimaryKey = new DataColumn[] {
                    key
                };
 
            _colUnique = key;
            return _colUnique;
        }
 
        internal DataColumn AddUniqueKey() {
            return AddUniqueKey(-1);
        }
 
        internal DataColumn AddForeignKey(DataColumn parentKey) {
            Debug.Assert(parentKey != null, "AddForeignKey: Invalid paramter.. related primary key is null");
 
            string      keyName = XMLSchema.GenUniqueColumnName(parentKey.ColumnName, this);
            DataColumn  foreignKey = new DataColumn(keyName, parentKey.DataType, null, MappingType.Hidden);
            Columns.Add(foreignKey);
 
            return foreignKey;
        }
 
        internal void UpdatePropertyDescriptorCollectionCache() {
            propertyDescriptorCollectionCache = null;
        }
 
        /// <devdoc>
        ///     Retrieves an array of properties that the given component instance
        ///     provides.  This may differ from the set of properties the class
        ///     provides.  If the component is sited, the site may add or remove
        ///     additional properties.  The returned array of properties will be
        ///     filtered by the given set of attributes.
        /// </devdoc>
        internal PropertyDescriptorCollection GetPropertyDescriptorCollection(Attribute[] attributes) {
            if (propertyDescriptorCollectionCache == null) {
                int columnsCount   = Columns.Count;
                int relationsCount = ChildRelations.Count;
                PropertyDescriptor[] props = new PropertyDescriptor[columnsCount + relationsCount]; {
                    for (int i = 0; i < columnsCount; i++) {
                        props[i] = new DataColumnPropertyDescriptor(Columns[i]);
                    }
                    for (int i = 0; i < relationsCount; i++) {
                        props[columnsCount + i] = new DataRelationPropertyDescriptor(ChildRelations[i]);
                    }
                }
                propertyDescriptorCollectionCache = new PropertyDescriptorCollection(props);
            }
            return propertyDescriptorCollectionCache;
        }
 
        internal XmlQualifiedName TypeName {
            get {
                return ((typeName == null) ? XmlQualifiedName.Empty : (XmlQualifiedName)typeName);
            }
            set {
                typeName = value;
            }
        }
 
        public void Merge(DataTable table)
        {
            Merge(table, false, MissingSchemaAction.Add);
        }
 
        public void Merge(DataTable table, bool preserveChanges)
        {
            Merge(table, preserveChanges, MissingSchemaAction.Add);
        }
 
        public void Merge(DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Merge|API> %d#, table=%d, preserveChanges=%d{bool}, missingSchemaAction=%d{ds.MissingSchemaAction}\n", ObjectID, (table != null) ? table.ObjectID : 0, preserveChanges, (int)missingSchemaAction);
            try{
                if (table == null)
                    throw ExceptionBuilder.ArgumentNull("table");
 
                switch(missingSchemaAction) { // @perfnote: Enum.IsDefined
                case MissingSchemaAction.Add:
                case MissingSchemaAction.Ignore:
                case MissingSchemaAction.Error:
                case MissingSchemaAction.AddWithKey:
                    Merger merger = new Merger(this, preserveChanges, missingSchemaAction);
                    merger.MergeTable(table);
                    break;
                default:
                    throw Common.ADP.InvalidMissingSchemaAction(missingSchemaAction);
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        public void Load (IDataReader reader){
            Load(reader, LoadOption.PreserveChanges, null);
        }
 
        public void Load (IDataReader reader, LoadOption loadOption) {
            Load(reader, loadOption, null);
        }
 
        public virtual void Load (IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler){
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.Load|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID, (int)loadOption);
            try {
                if (this.PrimaryKey.Length == 0) {
                    DataTableReader dtReader = reader as DataTableReader;
                    if (dtReader != null && dtReader.CurrentDataTable == this)
                        return; // if not return, it will go to infinite loop
                }
                Common.LoadAdapter adapter = new Common.LoadAdapter();
                adapter.FillLoadOption = loadOption;
                adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
                if (null != errorHandler) {
                    adapter.FillError += errorHandler;
                }
                adapter.FillFromReader(new DataTable[] { this }, reader, 0, 0);
 
                if (!reader.IsClosed && !reader.NextResult()) { // 
                    reader.Close();
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        private DataRow LoadRow(object[] values, LoadOption loadOption, Index searchIndex) {
            int recordNo;
            DataRow dataRow = null;
 
            if (searchIndex != null) {
                int[] primaryKeyIndex = new int[0];
                if (this.primaryKey != null) { // I do check above for PK, but in case if someone else gives me some index unrelated to PK
                    primaryKeyIndex = new int[this.primaryKey.ColumnsReference.Length];
                    for(int i = 0; i < this.primaryKey.ColumnsReference.Length; i++) {
                        primaryKeyIndex[i] = this.primaryKey.ColumnsReference[i].Ordinal;
                    }
                }
 
                object[] keys = new object[primaryKeyIndex.Length];
                for(int i = 0; i < primaryKeyIndex.Length; i++) {
                    keys[i] = values[primaryKeyIndex[i]];
                }
 
                Range result = searchIndex.FindRecords(keys);
 
                if (!result.IsNull) {
                    int deletedRowUpsertCount = 0;
                    for(int i = result.Min; i <= result.Max; i++) {
                        int resultRecord = searchIndex.GetRecord(i);
                        dataRow = this.recordManager[resultRecord];
                        recordNo = NewRecordFromArray(values);
 
                        //SQLBU DT 33648
                        // values array is being reused by DataAdapter, do not modify the values array
                        for(int count = 0; count < values.Length; count++) {
                            if (null == values[count]) {
                                columnCollection[count].Copy(resultRecord, recordNo);
                            }
                        }
                        for(int count = values.Length; count < columnCollection.Count ; count++) {
                            columnCollection[count].Copy(resultRecord, recordNo); // if there are missing values
                        }
 
                        if (loadOption != LoadOption.Upsert || dataRow.RowState != DataRowState.Deleted) {
                            SetDataRowWithLoadOption(dataRow , recordNo, loadOption, true);
                        }
                        else {
                            deletedRowUpsertCount++;
                        }
                    }
                    if (0 == deletedRowUpsertCount) {
                        return dataRow;
                    }
                }
            }
 
            recordNo = NewRecordFromArray(values);
            dataRow = NewRow(recordNo);
            // fire rowChanging event here
            DataRowAction action;
            DataRowChangeEventArgs drcevent = null;
            switch(loadOption) {
                case LoadOption.OverwriteChanges:
                case LoadOption.PreserveChanges:
                    action = DataRowAction.ChangeCurrentAndOriginal;
                    break;
                case LoadOption.Upsert:
                    action = DataRowAction.Add;
                    break;
                default:
                    throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
            }
 
            drcevent = RaiseRowChanging(null, dataRow, action);
 
            this.InsertRow (dataRow, -1, -1, false);
            switch(loadOption) {
                case LoadOption.OverwriteChanges:
                case LoadOption.PreserveChanges:
                    this.SetOldRecord(dataRow,  recordNo);
                    break;
                case LoadOption.Upsert:
                    break;
                default:
                    throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
            }
            RaiseRowChanged(drcevent, dataRow, action);
 
            return dataRow;
        }
 
        private void SetDataRowWithLoadOption (DataRow dataRow, int recordNo, LoadOption loadOption, bool checkReadOnly) {
            bool hasError = false;
            if (checkReadOnly) {
                foreach(DataColumn dc in this.Columns) {
                    if (dc.ReadOnly && !dc.Computed) {
                        switch(loadOption) {
                            case LoadOption.OverwriteChanges:
                                if ((dataRow[dc, DataRowVersion.Current] != dc[recordNo]) ||(dataRow[dc, DataRowVersion.Original] != dc[recordNo]))
                                    hasError = true;
                                break;
                            case LoadOption.Upsert:
                                if (dataRow[dc, DataRowVersion.Current] != dc[recordNo])
                                    hasError = true;
                                break;
                            case LoadOption.PreserveChanges:
                                if (dataRow[dc, DataRowVersion.Original] != dc[recordNo])
                                    hasError = true;
                                break;
                        }
                    }
                }
            } // No Event should be fired  in SenNewRecord and SetOldRecord
            // fire rowChanging event here
 
            DataRowChangeEventArgs drcevent = null;
            DataRowAction action = DataRowAction.Nothing;
            int cacheTempRecord = dataRow.tempRecord;
            dataRow.tempRecord = recordNo;
 
            switch(loadOption) {
                case LoadOption.OverwriteChanges:
                    action = DataRowAction.ChangeCurrentAndOriginal;
                    break;
                case LoadOption.Upsert:
                    switch(dataRow.RowState) {
                        case DataRowState.Unchanged:
                            // let see if the incomming value has the same values as existing row, so compare records
                            foreach(DataColumn dc in dataRow.Table.Columns) {
                                if (0 != dc.Compare(dataRow.newRecord, recordNo)) {
                                    action = DataRowAction.Change;
                                    break;
                                }
                            }
                            break;
                        case DataRowState.Deleted:
                            Debug.Assert(false, "LoadOption.Upsert with deleted row, should not be here");
                            break;
                        default :
                            action = DataRowAction.Change;
                            break;
                    }
                    break;
                case LoadOption.PreserveChanges:
                    switch(dataRow.RowState) {
                        case DataRowState.Unchanged:
                            action = DataRowAction.ChangeCurrentAndOriginal;
                            break;
                        default:
                            action = DataRowAction.ChangeOriginal;
                            break;
                    }
                    break;
                default:
                    throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
            }
 
            try {
                drcevent = RaiseRowChanging(null, dataRow, action);
                if (action == DataRowAction.Nothing) { // RaiseRowChanging does not fire for DataRowAction.Nothing
                    dataRow.inChangingEvent = true;
                    try {
                        drcevent = OnRowChanging(drcevent, dataRow, action);
                    }
                    finally {
                        dataRow.inChangingEvent = false;
                    }
                }
            }
            finally {
                Debug.Assert(dataRow.tempRecord == recordNo, "tempRecord has been changed in event handler");
                if (DataRowState.Detached == dataRow.RowState) {
                    // 'row.Table.Remove(row);'
                    if (-1 != cacheTempRecord) {
                        FreeRecord(ref cacheTempRecord);
                    }
                }
                else {
                    if (dataRow.tempRecord != recordNo) {
                        // 'row.EndEdit(); row.BeginEdit(); '
                        if (-1 != cacheTempRecord) {
                            FreeRecord(ref cacheTempRecord);
                        }
                        if (-1 != recordNo) {
                            FreeRecord(ref recordNo);
                        }
                        recordNo = dataRow.tempRecord;
                    }
                    else {
                        dataRow.tempRecord = cacheTempRecord;
                    }
                }
            }
            if (dataRow.tempRecord != -1) {
                dataRow.CancelEdit();
            }
 
            switch(loadOption) {
                case LoadOption.OverwriteChanges:
                     this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
                     this.SetOldRecord(dataRow,  recordNo);
                     break;
                case LoadOption.Upsert:
                     if (dataRow.RowState == DataRowState.Unchanged) {
                         this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
                         if (!dataRow.HasChanges()) {
                             this.SetOldRecord(dataRow, recordNo);
                         }
                     }
                     else {
                         if (dataRow.RowState == DataRowState.Deleted)
                             dataRow.RejectChanges();
                         this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
                     }
                     break;
                case LoadOption.PreserveChanges:
                     if (dataRow.RowState == DataRowState.Unchanged) {
                         // SQLBU 500706: DataTable internal index is corrupted: '8'
                         // if ListChanged event deletes dataRow
                         this.SetOldRecord(dataRow,  recordNo); // do not fire event
                         this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
                     }
                     else { // if modified/ added / deleted we want this operation to fire event (just for LoadOption.PreserveCurrentValues)
                        this.SetOldRecord(dataRow,  recordNo);
                     }
                     break;
                default:
                    throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
            }
 
            if (hasError) {
                string error = Res.GetString(Res.Load_ReadOnlyDataModified);
                if (dataRow.RowError.Length == 0) { // WebData 112272, append the row error
                    dataRow.RowError = error;
                }
                else {
                    dataRow.RowError += " ]:[ " + error ;
                }
 
                foreach(DataColumn dc in this.Columns) {
                    if (dc.ReadOnly && !dc.Computed)
                        dataRow.SetColumnError(dc, error);
                }
            }
 
            drcevent = RaiseRowChanged(drcevent, dataRow, action);
            if (action == DataRowAction.Nothing) { // RaiseRowChanged does not fire for DataRowAction.Nothing
                dataRow.inChangingEvent = true;
                try {
                    OnRowChanged(drcevent, dataRow, action);
                }
                finally {
                    dataRow.inChangingEvent = false;
                }
            }
 
        }
 
        public  DataTableReader CreateDataReader() {
            return new DataTableReader(this);
        }
 
 
        public void WriteXml(Stream stream)
        {
            WriteXml(stream, XmlWriteMode.IgnoreSchema, false);
        }
 
        public void WriteXml(Stream stream, bool writeHierarchy)
        {
            WriteXml(stream, XmlWriteMode.IgnoreSchema, writeHierarchy);
        }
 
        public void WriteXml(TextWriter writer)
        {
            WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
        }
 
        public void WriteXml(TextWriter writer, bool writeHierarchy)
        {
            WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
        }
 
        public void WriteXml(XmlWriter writer)
        {
            WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
        }
 
        public void WriteXml(XmlWriter writer, bool writeHierarchy)
        {
            WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXml(String fileName)
        {
            WriteXml(fileName, XmlWriteMode.IgnoreSchema, false);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXml(String fileName, bool writeHierarchy)
        {
            WriteXml(fileName, XmlWriteMode.IgnoreSchema, writeHierarchy);
        }
 
        public void WriteXml(Stream stream, XmlWriteMode mode)
        {
            WriteXml(stream, mode, false);
        }
 
        public void WriteXml(Stream stream, XmlWriteMode mode, bool writeHierarchy)
        {
            if (stream != null) {
                XmlTextWriter w =  new XmlTextWriter(stream, null) ;
                w.Formatting = Formatting.Indented;
 
                WriteXml( w, mode, writeHierarchy);
            }
        }
 
        public void WriteXml(TextWriter writer, XmlWriteMode mode)
        {
            WriteXml(writer, mode, false);
        }
 
        public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writeHierarchy)
        {
            if (writer != null) {
                XmlTextWriter w =  new XmlTextWriter(writer) ;
                w.Formatting = Formatting.Indented;
 
                WriteXml(w, mode, writeHierarchy);
            }
        }
 
        public void WriteXml(XmlWriter writer, XmlWriteMode mode)
        {
            WriteXml(writer, mode, false);
        }
        public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writeHierarchy)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, mode=%d{ds.XmlWriteMode}\n", ObjectID,  (int)mode);
            try{
                if (this.tableName.Length == 0) {
                    throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
                }
                // Generate SchemaTree and write it out
                if (writer != null) {
 
                    if (mode == XmlWriteMode.DiffGram) { // FIX THIS
                        // Create and save the updates
                        new NewDiffgramGen(this, writeHierarchy).Save(writer, this);
                    }
                    else {
                        // Create and save xml data
                        if (mode == XmlWriteMode.WriteSchema) {
                            DataSet ds = null;
                            string tablenamespace = this.tableNamespace;
                            if (null == this.DataSet) {
                                ds = new DataSet();
                                // if user set values on DataTable, it isn't necessary
                                // to set them on the DataSet because they won't be inherited
                                // but it is simpler to set them in both places
 
                                // if user did not set values on DataTable, it is required
                                // to set them on the DataSet so the table will inherit
                                // the value already on the Datatable
                                ds.SetLocaleValue(_culture, _cultureUserSet);
                                ds.CaseSensitive = this.CaseSensitive;
                                ds.Namespace = this.Namespace;
                                ds.RemotingFormat = this.RemotingFormat;
                                ds.Tables.Add(this);
                            }
 
                            if (writer != null) {
                                XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
                                xmldataWriter.Save(writer, /*mode == XmlWriteMode.WriteSchema*/true);
                            }
                            if (null != ds) {
                                ds.Tables.Remove(this);
                                this.tableNamespace = tablenamespace;
                            }
                        }
                        else {
                            XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
                            xmldataWriter.Save(writer,/*mode == XmlWriteMode.WriteSchema*/ false);
                        }
                    }
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXml(String fileName, XmlWriteMode mode)
        {
            WriteXml(fileName, mode, false);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXml(String fileName, XmlWriteMode mode, bool writeHierarchy)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, fileName='%ls', mode=%d{ds.XmlWriteMode}\n", ObjectID, fileName, (int)mode);
            try {
                using(XmlTextWriter xw = new XmlTextWriter( fileName, null )) {
                    xw.Formatting = Formatting.Indented;
                    xw.WriteStartDocument(true);
 
                    WriteXml(xw, mode, writeHierarchy);
 
                    xw.WriteEndDocument();
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        public void WriteXmlSchema(Stream stream) {
            WriteXmlSchema(stream, false);
        }
 
        public void WriteXmlSchema(Stream stream, bool writeHierarchy)
        {
            if (stream == null)
                return;
 
            XmlTextWriter w =  new XmlTextWriter(stream, null) ;
            w.Formatting = Formatting.Indented;
 
            WriteXmlSchema( w, writeHierarchy );
        }
 
        public void WriteXmlSchema( TextWriter writer ) {
            WriteXmlSchema( writer, false );
        }
 
        public void WriteXmlSchema( TextWriter writer, bool writeHierarchy )
        {
 
            if (writer == null)
                return;
 
            XmlTextWriter w =  new XmlTextWriter(writer);
            w.Formatting = Formatting.Indented;
 
            WriteXmlSchema( w, writeHierarchy );
        }
 
        private  bool CheckForClosureOnExpressions(DataTable dt, bool writeHierarchy) {
            List<DataTable> tableList = new List<DataTable>();
            tableList.Add(dt);
            if (writeHierarchy) { // WebData 112161
                CreateTableList(dt, tableList);
            }
            return CheckForClosureOnExpressionTables(tableList);
        }
 
        private bool CheckForClosureOnExpressionTables(List<DataTable> tableList) {
            Debug.Assert(tableList != null, "tableList shouldnot be null");
 
            foreach(DataTable datatable in tableList) {
                foreach(DataColumn dc in datatable.Columns) {
                    if (dc.Expression.Length != 0)  {
                        DataColumn[] dependency = dc.DataExpression.GetDependency();
                        for (int j = 0; j < dependency.Length; j++) {
                            if (!(tableList.Contains(dependency[j].Table))) {
                                return false;
                            }
                        }
                    }
                }
            }
            return true;
        }
 
 
        public void WriteXmlSchema(XmlWriter writer) {
            WriteXmlSchema(writer, false);
        }
 
        public void WriteXmlSchema(XmlWriter writer, bool writeHierarchy)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXmlSchema|API> %d#\n", ObjectID);
            try{
                if (this.tableName.Length == 0) {
                    throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
                }
 
                if (!CheckForClosureOnExpressions(this, writeHierarchy)) {
                    throw ExceptionBuilder.CanNotSerializeDataTableHierarchy();
                }
 
                DataSet ds = null;
                string tablenamespace = this.tableNamespace;//SQL BU Defect Tracking 286968
 
                // Generate SchemaTree and write it out
                if (null == this.DataSet) {
                    ds = new DataSet();
                    // if user set values on DataTable, it isn't necessary
                    // to set them on the DataSet because they won't be inherited
                    // but it is simpler to set them in both places
 
                    // if user did not set values on DataTable, it is required
                    // to set them on the DataSet so the table will inherit
                    // the value already on the Datatable
                    ds.SetLocaleValue(_culture, _cultureUserSet);
                    ds.CaseSensitive = this.CaseSensitive;
                    ds.Namespace = this.Namespace;
                    ds.RemotingFormat = this.RemotingFormat;
                    ds.Tables.Add(this);
                }
 
                if (writer != null) {
                    XmlTreeGen treeGen = new XmlTreeGen(SchemaFormat.Public);
                    treeGen.Save(null, this, writer, writeHierarchy);
                }
                if (null != ds) {
                    ds.Tables.Remove(this);
                    this.tableNamespace = tablenamespace;
                }
            }
            finally {
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXmlSchema(String fileName) {
            WriteXmlSchema(fileName, false);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void WriteXmlSchema(String fileName, bool writeHierarchy)
        {
            XmlTextWriter xw = new XmlTextWriter( fileName, null );
            try {
                xw.Formatting = Formatting.Indented;
                xw.WriteStartDocument(true);
                WriteXmlSchema(xw, writeHierarchy);
                xw.WriteEndDocument();
            }
            finally {
                xw.Close();
            }
        }
 
        public XmlReadMode ReadXml(Stream stream)
        {
            if (stream == null)
                return XmlReadMode.Auto;
 
            XmlTextReader xr = new XmlTextReader(stream);
 
            // Prevent Dtd entity in DataTable 
            xr.XmlResolver = null;
 
            return ReadXml(xr, false);
        }
 
        public XmlReadMode ReadXml(TextReader reader)
        {
            if (reader == null)
                return XmlReadMode.Auto;
 
            XmlTextReader xr = new XmlTextReader(reader);
 
            // Prevent Dtd entity in DataTable 
            xr.XmlResolver = null;
 
            return ReadXml(xr, false);
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public XmlReadMode ReadXml(string fileName)
        {
            XmlTextReader xr = new XmlTextReader(fileName);
 
            // Prevent Dtd entity in DataTable 
            xr.XmlResolver = null;
 
            try
            {
                return ReadXml( xr , false);
            }
            finally {
                xr.Close();
            }
        }
 
        public XmlReadMode ReadXml(XmlReader reader)
        {
            return ReadXml(reader, false);
        }
 
        private void RestoreConstraint(bool originalEnforceConstraint) {
            if (this.DataSet != null) {
                this.DataSet.EnforceConstraints = originalEnforceConstraint;
            }
            else {
                this.EnforceConstraints = originalEnforceConstraint;
            }
        }
 
        private bool IsEmptyXml(XmlReader reader) {
            if (reader.IsEmptyElement) {
                if (reader.AttributeCount == 0 || (reader.LocalName == Keywords.DIFFGRAM && reader.NamespaceURI == Keywords.DFFNS)) {
                    return true;
                }
                if (reader.AttributeCount == 1) {
                    reader.MoveToAttribute(0);
                    if ((this.Namespace == reader.Value) &&
                        (this.Prefix == reader.LocalName) &&
                        (reader.Prefix == Keywords.XMLNS) &&
                        (reader.NamespaceURI == Keywords.XSD_XMLNS_NS))
                    return true;
                }
            }
            return false;
        }
 
        internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXml|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
            try {
 
              DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
              try {
                bool fDataFound = false;
                bool fSchemaFound = false;
                bool fDiffsFound = false;
                bool fIsXdr = false;
                int iCurrentDepth = -1;
                XmlReadMode ret = XmlReadMode.Auto;
 
                // clear the hashtable to avoid conflicts between diffgrams, SqlHotFix 782
                rowDiffIdUsage.Prepare(this);
 
                if (reader == null)
                    return ret;
                bool originalEnforceConstraint = false;
                if (this.DataSet != null) {
                    originalEnforceConstraint = this.DataSet.EnforceConstraints;
                    this.DataSet.EnforceConstraints = false;
                }
                else {
                    originalEnforceConstraint = this.EnforceConstraints;
                    this.EnforceConstraints = false;
                }
 
                if (reader is XmlTextReader)
                    ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
 
                XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
                XmlDataLoader xmlload = null;
 
 
                reader.MoveToContent();
                if (Columns.Count == 0) {
                    if (IsEmptyXml(reader)) {
                        reader.Read();
                        return ret;
                    }
                }
 
                if (reader.NodeType == XmlNodeType.Element) {
                    iCurrentDepth = reader.Depth;
 
                    if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
                        if (Columns.Count == 0) {
                            if (reader.IsEmptyElement) {
                                reader.Read();
                                return XmlReadMode.DiffGram;
                            }
                            throw ExceptionBuilder.DataTableInferenceNotSupported();
                        }
                        this.ReadXmlDiffgram(reader);
                        // read the closing tag of the current element
                        ReadEndElement(reader);
 
                        RestoreConstraint(originalEnforceConstraint);
                        return XmlReadMode.DiffGram;
                    }
 
                    // if reader points to the schema load it
                    if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
                        // load XDR schema and exit
                        ReadXDRSchema(reader);
 
                        RestoreConstraint(originalEnforceConstraint);
                        return XmlReadMode.ReadSchema; //since the top level element is a schema return
                    }
 
                    if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
                        // load XSD schema and exit
                        ReadXmlSchema(reader, denyResolving);
                        RestoreConstraint(originalEnforceConstraint);
                        return XmlReadMode.ReadSchema; //since the top level element is a schema return
                    }
 
                    if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
                        if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
                            this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
                        }
                        else {
                            this.enforceConstraints = originalEnforceConstraint;
                        }
 
                        throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
                    }
 
                    // now either the top level node is a table and we load it through dataReader...
 
                    // ... or backup the top node and all its attributes because we may need to InferSchema
                    XmlElement topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    if (reader.HasAttributes) {
                        int attrCount = reader.AttributeCount;
                        for (int i=0;i<attrCount;i++) {
                            reader.MoveToAttribute(i);
                            if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
                                topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
                            else {
                                XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
                                attr.Prefix = reader.Prefix;
                                attr.Value = reader.GetAttribute(i);
                            }
                        }
                    }
                    reader.Read();
 
                    while(MoveToElement(reader, iCurrentDepth)) {
 
                        if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
                            this.ReadXmlDiffgram(reader);
                            // read the closing tag of the current element
                            ReadEndElement(reader);
                            RestoreConstraint(originalEnforceConstraint);
                            return XmlReadMode.DiffGram;
                        }
 
                        // if reader points to the schema load it...
 
 
                        if (!fSchemaFound && !fDataFound && reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
                            // load XDR schema and exit
                            ReadXDRSchema(reader);
                            fSchemaFound = true;
                            fIsXdr = true;
                            continue;
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
                            // load XSD schema and exit
                            ReadXmlSchema(reader, denyResolving);
                            fSchemaFound = true;
                            continue;
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
                            if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
                                this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
                            }
                            else {
                                this.enforceConstraints = originalEnforceConstraint;
                            }
                            throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
                        }
 
                        if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
                            this.ReadXmlDiffgram(reader);
                            fDiffsFound = true;
                            ret = XmlReadMode.DiffGram;
                        }
                        else {
                            // we found data here
                            fDataFound = true;
 
                            if (!fSchemaFound && Columns.Count == 0) {
                                XmlNode node = xdoc.ReadNode(reader);
                                topNode.AppendChild(node);
                            }
                            else {
                                if (xmlload == null)
                                    xmlload = new XmlDataLoader(this, fIsXdr, topNode, false);
                                xmlload.LoadData(reader);
                                if (fSchemaFound)
                                    ret = XmlReadMode.ReadSchema;
                                else
                                    ret = XmlReadMode.IgnoreSchema;
                            }
                        }
 
                    }
                    // read the closing tag of the current element
                    ReadEndElement(reader);
 
                    // now top node contains the data part
                    xdoc.AppendChild(topNode);
 
                    if (!fSchemaFound && Columns.Count == 0) {
                        if (IsEmptyXml(reader)) {
                            reader.Read();
                            return ret;
                        }
                        throw ExceptionBuilder.DataTableInferenceNotSupported();
                    }
 
                    if (xmlload == null)
                        xmlload = new XmlDataLoader(this, fIsXdr, false);
 
                    // so we InferSchema
                    if (!fDiffsFound) {// we need to add support for inference here
                    }
                }
                RestoreConstraint(originalEnforceConstraint);
                return ret;
              }
              finally {
                rowDiffIdUsage.Cleanup();
              }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolving)
        {
            DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
            try {
                bool fSchemaFound = false;
                bool fDataFound = false;
                bool fIsXdr = false;
                int iCurrentDepth = -1;
                XmlReadMode ret = mode;
 
                // Dev11 904428: prepare and cleanup rowDiffId hashtable
                rowDiffIdUsage.Prepare(this);
 
                if (reader == null)
                    return ret;
 
                bool originalEnforceConstraint  = false;
                if (this.DataSet != null) {
                    originalEnforceConstraint  = this.DataSet.EnforceConstraints;
                    this.DataSet.EnforceConstraints = false;
                }
                else {
                    originalEnforceConstraint  = this.EnforceConstraints;
                    this.EnforceConstraints = false;
                }
 
                if (reader is XmlTextReader)
                    ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
 
                XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
 
                if ((mode != XmlReadMode.Fragment) && (reader.NodeType == XmlNodeType.Element))
                    iCurrentDepth = reader.Depth;
 
                reader.MoveToContent();
                if (Columns.Count == 0) {
                    if (IsEmptyXml(reader)) {
                        reader.Read();
                        return ret;
                    }
                }
 
                XmlDataLoader xmlload = null;
 
                if (reader.NodeType == XmlNodeType.Element) {
                    XmlElement topNode = null;
                    if (mode == XmlReadMode.Fragment) {
                        xdoc.AppendChild(xdoc.CreateElement("ds_sqlXmlWraPPeR"));
                        topNode = xdoc.DocumentElement;
                    }
                    else { //handle the top node
                        if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
                            if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
                                if (Columns.Count == 0) {
                                    if(reader.IsEmptyElement) {
                                        reader.Read();
                                        return XmlReadMode.DiffGram;
                                    }
                                    throw ExceptionBuilder.DataTableInferenceNotSupported();
                                }
                                this.ReadXmlDiffgram(reader);
                                // read the closing tag of the current element
                                ReadEndElement(reader);
                            }
                            else {
                                reader.Skip();
                            }
                            RestoreConstraint(originalEnforceConstraint);
                            return ret;
                        }
 
                        if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
                            // load XDR schema and exit
                            if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
                                ReadXDRSchema(reader);
                            }
                            else {
                                reader.Skip();
                            }
                            RestoreConstraint(originalEnforceConstraint);
                            return ret; //since the top level element is a schema return
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
                            // load XSD schema and exit
                            if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
                                ReadXmlSchema(reader, denyResolving);
                            }
                            else
                                reader.Skip();
                            RestoreConstraint(originalEnforceConstraint);
                            return ret; //since the top level element is a schema return
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
                             if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
                                 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
                             }
                             else {
                                this.enforceConstraints = originalEnforceConstraint;
                            }
                            throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
                        }
 
                        // now either the top level node is a table and we load it through dataReader...
                        // ... or backup the top node and all its attributes
                        topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                        if (reader.HasAttributes) {
                            int attrCount = reader.AttributeCount;
                            for (int i=0;i<attrCount;i++) {
                                reader.MoveToAttribute(i);
                                if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
                                    topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
                                else {
                                    XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
                                    attr.Prefix = reader.Prefix;
                                    attr.Value = reader.GetAttribute(i);
                                }
                            }
                        }
                        reader.Read();
                    }
 
                    while(MoveToElement(reader, iCurrentDepth)) {
 
                        if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
                            // load XDR schema
                       	    if (!fSchemaFound && !fDataFound && (mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
                                ReadXDRSchema(reader);
                                fSchemaFound = true;
                                fIsXdr = true;
                            }
                            else {
                                reader.Skip();
                            }
                            continue;
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
                        // load XSD schema and exit
                            if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
                                ReadXmlSchema(reader, denyResolving);
                                fSchemaFound = true;
                            }
                            else {
                                reader.Skip();
                            }
                            continue;
                        }
 
                        if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
                            if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
                                if (Columns.Count == 0) {
                                    if(reader.IsEmptyElement) {
                                        reader.Read();
                                        return XmlReadMode.DiffGram;
                                    }
                                    throw ExceptionBuilder.DataTableInferenceNotSupported();
                                }
                                this.ReadXmlDiffgram(reader);
                                ret = XmlReadMode.DiffGram;
                            }
                            else {
                                reader.Skip();
                            }
                            continue;
                        }
 
                        if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
                            if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
                                 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
                            }
                            else {
                                this.enforceConstraints = originalEnforceConstraint;
                            }
                            throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
                        }
 
                        if (mode == XmlReadMode.DiffGram) {
                            reader.Skip();
                            continue; // we do not read data in diffgram mode
                        }
 
                        // if we are here we found some data
                        fDataFound = true;
 
                        if (mode == XmlReadMode.InferSchema) { //save the node in DOM until the end;
                            XmlNode node = xdoc.ReadNode(reader);
                            topNode.AppendChild(node);
                        }
                        else {
                            if (Columns.Count == 0) {
                                throw ExceptionBuilder.DataTableInferenceNotSupported();
                            }
                            if (xmlload == null)
                                xmlload = new XmlDataLoader(this, fIsXdr, topNode,  mode == XmlReadMode.IgnoreSchema);
                            xmlload.LoadData(reader);
                        }
                    } //end of the while
 
                    // read the closing tag of the current element
                    ReadEndElement(reader);
 
                    // now top node contains the data part
                    xdoc.AppendChild(topNode);
 
                    if (xmlload == null)
                        xmlload = new XmlDataLoader(this, fIsXdr, mode == XmlReadMode.IgnoreSchema);
 
                    if (mode == XmlReadMode.DiffGram) {
                        // we already got the diffs through XmlReader interface
                        RestoreConstraint(originalEnforceConstraint);
                        return ret;
                    }
    //todo
                    // Load Data
                    if (mode == XmlReadMode.InferSchema) {
                        if (Columns.Count == 0) {
                            throw ExceptionBuilder.DataTableInferenceNotSupported();
                        }
 
    // Microsoft                    xmlload.InferSchema(xdoc, null);
    // Microsoft                    xmlload.LoadData(xdoc);
                    }
                }
                RestoreConstraint(originalEnforceConstraint);
 
                return ret;
            }
            finally {
                // Dev11 904428: prepare and cleanup rowDiffId hashtable
                rowDiffIdUsage.Cleanup();
            }
        }
 
 
        internal void ReadEndElement(XmlReader reader) {
            while (reader.NodeType == XmlNodeType.Whitespace) {
                reader.Skip();
            }
            if (reader.NodeType == XmlNodeType.None) {
                reader.Skip();
            }
            else if (reader.NodeType == XmlNodeType.EndElement) {
                reader.ReadEndElement();
            }
        }
        internal void ReadXDRSchema(XmlReader reader) {
            XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
            XmlNode schNode = xdoc.ReadNode(reader);;
            //consume and ignore it - No support
        }
 
        internal bool MoveToElement(XmlReader reader, int depth) {
            while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.Element && reader.Depth > depth) {
                reader.Read();
            }
            return (reader.NodeType == XmlNodeType.Element);
        }
        private void ReadXmlDiffgram(XmlReader reader) { // fill correctly
            int d = reader.Depth;
            bool fEnforce = this.EnforceConstraints;
            this.EnforceConstraints =false;
            DataTable newDt;
            bool isEmpty;
 
            if (this.Rows.Count == 0) {
                isEmpty = true;
                newDt = this;
            }
            else {
                isEmpty = false;
                newDt = this.Clone();
                newDt.EnforceConstraints = false;
            }
 
            newDt.Rows.nullInList = 0;
 
            reader.MoveToContent();
 
            if ((reader.LocalName != Keywords.DIFFGRAM) && (reader.NamespaceURI != Keywords.DFFNS))
                return;
            reader.Read();
            if (reader.NodeType == XmlNodeType.Whitespace) {
                MoveToElement(reader, reader.Depth - 1 /*iCurrentDepth*/); // skip over whitespaces.
            }
 
            newDt.fInLoadDiffgram = true;
 
            if (reader.Depth > d) {
                if ((reader.NamespaceURI != Keywords.DFFNS) && (reader.NamespaceURI != Keywords.MSDNS)) {
                    //we should be inside the dataset part
                    XmlDocument xdoc = new XmlDocument();
                    XmlElement node = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    reader.Read();
                    if (reader.Depth-1 > d) {
                        XmlDataLoader xmlload = new XmlDataLoader(newDt, false, node, false);
                        xmlload.isDiffgram = true; // turn on the special processing
                        xmlload.LoadData(reader);
                    }
                    ReadEndElement(reader);
                }
                if (((reader.LocalName == Keywords.SQL_BEFORE) && (reader.NamespaceURI == Keywords.DFFNS)) ||
                    ((reader.LocalName == Keywords.MSD_ERRORS) && (reader.NamespaceURI == Keywords.DFFNS)))
 
                {
                    //this will consume the changes and the errors part
                    XMLDiffLoader diffLoader = new XMLDiffLoader();
                    diffLoader.LoadDiffGram(newDt, reader);
                }
 
                // get to the closing diff tag
                while(reader.Depth > d) {
                    reader.Read();
                }
                // read the closing tag
                ReadEndElement(reader);
            }
 
            if (newDt.Rows.nullInList > 0)
                throw ExceptionBuilder.RowInsertMissing(newDt.TableName);
 
 
            newDt.fInLoadDiffgram = false;
            List<DataTable> tableList = new List<DataTable>();
            tableList.Add(this);
            CreateTableList(this, tableList);
 
// this is terrible, optimize it
            for (int i = 0; i < tableList.Count ; i++) {
                DataRelation[] relations = tableList[i].NestedParentRelations;
                foreach(DataRelation rel in relations) {
                    if (rel != null && rel.ParentTable == tableList[i]) {
                        foreach (DataRow r in tableList[i].Rows) {
                            foreach (DataRelation rel2 in relations) {
                                r.CheckForLoops(rel2);
                            }
                        }
                    }
                }
            }
 
            if (!isEmpty) {
                this.Merge(newDt);
            }
            this.EnforceConstraints = fEnforce;
        }
 
        internal void ReadXSDSchema(XmlReader reader, bool denyResolving) {
            XmlSchemaSet sSet = new XmlSchemaSet();
            while (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
                XmlSchema s = XmlSchema.Read(reader, null);
                sSet.Add(s);
                //read the end tag
                ReadEndElement(reader);
            }
            sSet.Compile();
 
            XSDSchema schema = new XSDSchema();
            schema.LoadSchema(sSet, this);
        }
 
 
        public void ReadXmlSchema(Stream stream)
        {
            if (stream == null)
                return;
 
            ReadXmlSchema( new XmlTextReader( stream ), false );
        }
 
        public void ReadXmlSchema(TextReader reader)
        {
            if (reader == null)
                return;
 
            ReadXmlSchema( new XmlTextReader( reader ), false );
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public void ReadXmlSchema(String fileName)
        {
            XmlTextReader xr = new XmlTextReader(fileName);
            try {
                ReadXmlSchema( xr, false );
            }
            finally {
                xr.Close();
            }
        }
 
        public void ReadXmlSchema(XmlReader reader)
        {
            ReadXmlSchema(reader, false);
        }
 
        internal void ReadXmlSchema(XmlReader reader, bool denyResolving)
        {
            IntPtr hscp;
            Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXmlSchema|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
            try{
                DataSet ds = new DataSet();
                SerializationFormat cachedRemotingFormat = this.RemotingFormat;
                // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
                ds.ReadXmlSchema(reader, denyResolving);
 
                string CurrentTableFullName = ds.MainTableName;
 
                if (Common.ADP.IsEmpty(this.tableName) && Common.ADP.IsEmpty(CurrentTableFullName))
                    return;
 
                DataTable currentTable = null;
 
                if (!Common.ADP.IsEmpty(this.tableName)) {
                    if (!Common.ADP.IsEmpty(this.Namespace)) {
                        currentTable = ds.Tables[this.tableName, this.Namespace];
                    }
                    else {//SQL BU defect tracking 240293
                        int tableIndex = ds.Tables.InternalIndexOf(this.tableName);
                        if (tableIndex  > -1) {
                            currentTable = ds.Tables[tableIndex];
                        }
                    }
                }
                else{  //!Common.ADP.IsEmpty(CurrentTableFullName)
                    string CurrentTableNamespace = "";
                    int nsSeperator = CurrentTableFullName.IndexOf(':');
                    if (nsSeperator > -1) {
                        CurrentTableNamespace = CurrentTableFullName.Substring(0, nsSeperator);
                    }
                    string CurrentTableName = CurrentTableFullName.Substring(nsSeperator + 1, CurrentTableFullName.Length - nsSeperator -1);
 
                    currentTable = ds.Tables[CurrentTableName, CurrentTableNamespace];
                }
 
                if (currentTable == null) { // bug fix :99186
                    string qTableName = string.Empty;
                    if (!Common.ADP.IsEmpty(this.tableName)) {
                        qTableName = (this.Namespace.Length > 0)? (this.Namespace + ":" + this.tableName):this.tableName;
                    }
                    else {
                        qTableName = CurrentTableFullName ;
                    }
                    throw ExceptionBuilder.TableNotFound(qTableName);
                }
 
                currentTable._remotingFormat = cachedRemotingFormat;
 
                List<DataTable> tableList = new List<DataTable>();
                tableList.Add(currentTable);
                CreateTableList(currentTable, tableList);
                List<DataRelation> relationList = new List<DataRelation>();
                CreateRelationList(tableList, relationList);
 
                if (relationList.Count == 0) {
                    if (this.Columns.Count == 0) {
                        DataTable tempTable = currentTable;
                        if (tempTable != null)
                            tempTable.CloneTo(this, null, false); // we may have issue Amir
                        if (this.DataSet == null && this.tableNamespace == null) { // webdata 105506
// for standalone table, clone wont get these correctly, since they may come with inheritance
                            this.tableNamespace =  tempTable.Namespace;
                        }
                    }
                    return;
                }
                else {
                    if (Common.ADP.IsEmpty(this.TableName)) {
                        this.TableName = currentTable.TableName;
                        if (!Common.ADP.IsEmpty(currentTable.Namespace)) {
                            this.Namespace = currentTable.Namespace;
                        }
                    }
                    if (this.DataSet == null) {
                        DataSet dataset = new DataSet(ds.DataSetName);
// webdata 105506
                        // if user set values on DataTable, it isn't necessary
                        // to set them on the DataSet because they won't be inherited
                        // but it is simpler to set them in both places
 
                        // if user did not set values on DataTable, it is required
                        // to set them on the DataSet so the table will inherit
                        // the value already on the Datatable
                        dataset.SetLocaleValue(ds.Locale, ds.ShouldSerializeLocale());
                        dataset.CaseSensitive = ds.CaseSensitive;
                        dataset.Namespace = ds.Namespace;
                        dataset.mainTableName = ds.mainTableName;
                        dataset.RemotingFormat = ds.RemotingFormat;
 
                        dataset.Tables.Add(this);
                    }
 
                    DataTable targetTable = CloneHierarchy(currentTable, this.DataSet, null);
 
                    foreach(DataTable tempTable in tableList) {
                        DataTable destinationTable = this.DataSet.Tables[tempTable.tableName, tempTable.Namespace];
                        DataTable sourceTable = ds.Tables[tempTable.tableName, tempTable.Namespace];
                        foreach(Constraint tempConstrain in sourceTable.Constraints) {
                            ForeignKeyConstraint fkc = tempConstrain as ForeignKeyConstraint;  // we have already cloned the UKC when cloning the datatable
                            if (fkc != null) {
                                if (fkc.Table != fkc.RelatedTable)  {
                                    if (tableList.Contains(fkc.Table) && tableList.Contains(fkc.RelatedTable)) {
                                        ForeignKeyConstraint newFKC = (ForeignKeyConstraint)fkc.Clone(destinationTable.DataSet);
                                        if (!destinationTable.Constraints.Contains(newFKC.ConstraintName))
                                            destinationTable.Constraints.Add(newFKC); // we know that the dest table is already in the table
                                    }
                                }
                           }
                        }
                    }
                    foreach(DataRelation rel in relationList) {
                        if (!this.DataSet.Relations.Contains(rel.RelationName))
                            this.DataSet.Relations.Add(rel.Clone(this.DataSet));
                    }
 
                    bool hasExternaldependency = false;
 
                    foreach(DataTable tempTable in tableList) {
                        foreach(DataColumn dc in tempTable.Columns) {
                            hasExternaldependency = false;
                            if (dc.Expression.Length  != 0) {
                                DataColumn[] dependency = dc.DataExpression.GetDependency();
                                for (int j = 0; j < dependency.Length; j++) {
                                    if (!tableList.Contains(dependency[j].Table)) {
                                        hasExternaldependency = true;
                                        break;
                                    }
                                }
                            }
                            if (!hasExternaldependency) {
                                this.DataSet.Tables[tempTable.TableName, tempTable.Namespace].Columns[dc.ColumnName].Expression = dc.Expression;
                            }
                        }
                        hasExternaldependency = false;
                    }
 
                }
            }
            finally{
                Bid.ScopeLeave(ref hscp);
            }
        }
 
        private void CreateTableList(DataTable currentTable, List<DataTable> tableList) {
            foreach( DataRelation r in currentTable.ChildRelations ) {
                if (! tableList.Contains(r.ChildTable)) {
                    tableList.Add(r.ChildTable);
                    CreateTableList(r.ChildTable, tableList);
                }
            }
        }
        private void CreateRelationList(List<DataTable> tableList,  List<DataRelation> relationList) {
            foreach(DataTable table in tableList) {
                foreach( DataRelation r in table.ChildRelations) {
                    if (tableList.Contains(r.ChildTable) && tableList.Contains(r.ParentTable)) {
                        relationList.Add(r);
                    }
                }
            }
        }
 
        /**************************************************************************
        The V2.0 (no V1.0 or V1.1) WSDL for Untyped DataTable being returned as a result (no parameters)
        <s:element name="anyUserSpecifiedMethodName">
            <!--  This is where parameters go -->
            <s:complexType /> 
        </s:element>
        <s:element name="anyUserSpecifiedMethodName"+"Response">
            <s:complexType>
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="anyUserSpecifiedMethodName"+"Result">
                        <s:complexType>
                            <s:sequence>
                                <s:any minOccurs="0" maxOccurs="unbounded" namespace="http://www.w3.org/2001/XMLSchema" processContents="lax" /> 
                                <s:any minOccurs="1" namespace="urn:schemas-microsoft-com:xml-diffgram-v1" processContents="lax" /> 
                            </s:sequence>
                        </s:complexType>
                    </s:element>
                </s:sequence>
            </s:complexType>
        </s:element>
 
        Typed DataTable is not supported in WSDL (SQLBU 444636)
 
        either fails because xsd generates its typed DataTable with an internal parameterless ctor
        
        or System.NullReferenceException: Object reference not set to an instance of an object.   (if namespace of StronglyTyped DataTable is not set)
           at System.Data.XmlTreeGen.FindTargetNamespace(DataTable table)
 
        or System.InvalidOperationException: Schema Id is missing. The schema returned from WebServiceDataSetServer.Service+StudentsDataTable.GetSchema() must have an Id.
           at System.Xml.Serialization.SerializableMapping.RetrieveSerializableSchema()
        *****************************************************************************/
        public static XmlSchemaComplexType GetDataTableSchema(XmlSchemaSet schemaSet) {
            XmlSchemaComplexType type = new XmlSchemaComplexType();
            XmlSchemaSequence sequence = new XmlSchemaSequence();
            XmlSchemaAny any = new XmlSchemaAny();
            any.Namespace = XmlSchema.Namespace;
            any.MinOccurs = 0;
            any.MaxOccurs = Decimal.MaxValue;
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            sequence.Items.Add(any);
 
            any = new XmlSchemaAny();
            any.Namespace = Keywords.DFFNS;
            any.MinOccurs = 1; // when recognizing WSDL - MinOccurs="0" denotes DataSet, a MinOccurs="1" for DataTable
            any.ProcessContents = XmlSchemaContentProcessing.Lax;
            sequence.Items.Add(any);
 
            type.Particle = sequence;
 
            return type;
        }
 
        XmlSchema IXmlSerializable.GetSchema() {
            return GetSchema();
        }
 
        protected virtual XmlSchema GetSchema() {
            if (GetType() == typeof(DataTable)) {
                return null;
            }
            MemoryStream stream = new MemoryStream();
 
            XmlWriter writer = new XmlTextWriter(stream, null);
            if (writer != null) {
                 (new XmlTreeGen(SchemaFormat.WebService)).Save(this, writer);
            }
            stream.Position = 0;
            return XmlSchema.Read(new XmlTextReader(stream), null);
        }
 
        void IXmlSerializable.ReadXml(XmlReader reader) {
            IXmlTextParser textReader = reader as IXmlTextParser;
            bool fNormalization = true;
            if (textReader != null) {
                fNormalization = textReader.Normalized;
                textReader.Normalized = false;
            }
            ReadXmlSerializable(reader);
 
            if (textReader != null) {
                textReader.Normalized = fNormalization;
            }
        }
 
        void IXmlSerializable.WriteXml(XmlWriter writer) {
            WriteXmlSchema(writer, false);
            WriteXml(writer, XmlWriteMode.DiffGram, false);
        }
 
         protected virtual void ReadXmlSerializable(XmlReader reader) {
            ReadXml(reader, XmlReadMode.DiffGram, true);
        }
 
/*
        [
        DefaultValue(false),
        ResCategoryAttribute(Res.DataCategory_Data),
        ResDescriptionAttribute(Res.DataTableSerializeHierarchy)
        ]
        public bool SerializeHierarchy {
            get {
                return this.serializeHierarchy;
            }
            set {
                this.serializeHierarchy = value;
            }
        }
*/
 
        // RowDiffIdUsageSection & DSRowDiffIdUsageSection Usage:
        //
        //        DataTable.[DS]RowDiffIdUsageSection rowDiffIdUsage = new DataTable.[DS]RowDiffIdUsageSection();
        //        try {
        //            rowDiffIdUsage.Prepare(DataTable or DataSet, depending on type);
        //
        //            // code that requires RowDiffId usage
        //
        //        }
        //        finally {
        //            rowDiffIdUsage.Cleanup();
        //        }
        // 
        // Nested calls are allowed on different tables. For example, user can register to row change events and trigger 
        // ReadXml on different table/ds). But, if user code will try to call ReadXml on table that is already in the scope,
        // this code will assert since nested calls on same table are unsupported.
        internal struct RowDiffIdUsageSection
        {
#if DEBUG
            // This list contains tables currently used in diffgram processing, not including new tables that might be added later during.
            // if diffgram processing is not started, this value must be null. when it starts, relevant method should call Prepare.
            // Notes:
            // * in case of ReadXml on empty DataSet, this list can be initialized as empty (so empty list != null).
            // * only one scope is allowed on single thread, either for datatable or dataset
            // * assert is triggered if same table is added to this list twice
            // 
            // do not allocate TLS data in RETAIL bits!
            [ThreadStatic]
            internal static List<DataTable> s_usedTables;
#endif //DEBUG
 
            DataTable _targetTable;
 
            internal void Prepare(DataTable table)
            {
                Debug.Assert(_targetTable == null, "do not reuse this section");
                Debug.Assert(table != null);
                Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
#if DEBUG
                Debug.Assert(s_usedTables == null || !s_usedTables.Contains(table), 
                    "Nested call with same table can cause data corruption!");
#endif //DEBUG
 
#if DEBUG
                if (s_usedTables == null)
                    s_usedTables = new List<DataTable>();
                s_usedTables.Add(table);
#endif //DEBUG
                _targetTable = table;
                table.rowDiffId = null;
            }
 
            [Conditional("DEBUG")]
            internal void Cleanup()
            {
                // cannot assume target table was set - ThreadAbortException can be raised before Start is called
                if (_targetTable != null)
                {
#if DEBUG
                    Debug.Assert(s_usedTables != null && s_usedTables.Contains(_targetTable), "missing Prepare before Cleanup");
                    if (s_usedTables != null)
                    {
                        s_usedTables.Remove(_targetTable);
                        if (s_usedTables.Count == 0)
                            s_usedTables = null;
                    }
#endif //DEBUG
                    _targetTable.rowDiffId = null;
                }
            }
 
            [Conditional("DEBUG")]
            internal static void Assert(string message)
            {
#if DEBUG
                // this code asserts scope was created, but it does not assert that the table was included in it
                // note that in case of DataSet, new tables might be added to the list in which case they won't appear in s_usedTables.
                Debug.Assert(s_usedTables != null, message);
#endif //DEBUG
            }
        }
 
        internal struct DSRowDiffIdUsageSection
        {
            DataSet _targetDS;
 
            internal void Prepare(DataSet ds)
            {
                Debug.Assert(_targetDS == null, "do not reuse this section");
                Debug.Assert(ds != null);
 
                _targetDS = ds;
#if DEBUG
                // initialize list of tables out of current tables
                // note: it might remain empty (still initialization is needed for assert to operate)
                if (RowDiffIdUsageSection.s_usedTables == null)
                    RowDiffIdUsageSection.s_usedTables = new List<DataTable>();
#endif //DEBUG
                for (int tableIndex = 0; tableIndex < ds.Tables.Count; ++tableIndex)
                {
                    DataTable table = ds.Tables[tableIndex];
#if DEBUG
                    Debug.Assert(!RowDiffIdUsageSection.s_usedTables.Contains(table), "Nested call with same table can cause data corruption!");
                    RowDiffIdUsageSection.s_usedTables.Add(table);
#endif //DEBUG
                    Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
                    table.rowDiffId = null;
                }
            }
 
            [Conditional("DEBUG")]
            internal void Cleanup()
            {
                // cannot assume target was set - ThreadAbortException can be raised before Start is called
                if (_targetDS != null)
                {
#if DEBUG
                    Debug.Assert(RowDiffIdUsageSection.s_usedTables != null, "missing Prepare before Cleanup");
#endif //DEBUG
 
                    for (int tableIndex = 0; tableIndex < _targetDS.Tables.Count; ++tableIndex)
                    {
                        DataTable table = _targetDS.Tables[tableIndex];
#if DEBUG
                        // cannot assert that table exists in the usedTables - new tables might be 
                        // created during diffgram processing in DataSet.ReadXml.
                        if (RowDiffIdUsageSection.s_usedTables != null)
                            RowDiffIdUsageSection.s_usedTables.Remove(table);
#endif //DEBUG
                        table.rowDiffId = null;
                    }
#if DEBUG
                    if (RowDiffIdUsageSection.s_usedTables != null && RowDiffIdUsageSection.s_usedTables.Count == 0)
                        RowDiffIdUsageSection.s_usedTables = null; // out-of-scope
#endif //DEBUG
                }
            }
        }
 
        internal Hashtable RowDiffId {
            get {
                // assert scope has been created either with RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare
                RowDiffIdUsageSection.Assert("missing call to RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare");
 
                if (rowDiffId == null)
                    rowDiffId = new Hashtable();
                return rowDiffId;
            }
        }
 
        internal int ObjectID {
            get {
                return _objectID;
            }
        }
 
        internal void AddDependentColumn(DataColumn expressionColumn) {
            if (dependentColumns == null)
                dependentColumns = new List<DataColumn>();
 
            if (!dependentColumns.Contains(expressionColumn)) {
                // only remember unique columns but expect non-unique columns to be added
                dependentColumns.Add(expressionColumn);
            }
        }
 
        internal void RemoveDependentColumn(DataColumn expressionColumn) {
            if (dependentColumns != null && dependentColumns.Contains(expressionColumn)) {
                dependentColumns.Remove(expressionColumn);
            }
        }
 
        internal void EvaluateExpressions() {
            //evaluates all expressions for all rows in table
            // SQLBU 414992: Serious performance issue when calling Merge
            // this improves performance by only computing expressions when they are present
            // and iterating over the rows instead of computing their position multiple times
            if ((null != dependentColumns) && (0 < dependentColumns.Count)) {
                foreach(DataRow row in Rows) {
                    // only evaluate original values if different from current.
                    if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
                        EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, null);
                    }
                    if (row.newRecord != -1) {
                        EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, null);
                    }
                    if (row.tempRecord != -1) {
                        EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, null);
                    }
                }
            }
        }
 
        internal void EvaluateExpressions(DataRow row, DataRowAction action, List<DataRow> cachedRows) {
            // evaluate all expressions for specified row
            if (action == DataRowAction.Add ||
                action == DataRowAction.Change||
                (action == DataRowAction.Rollback && (row.oldRecord!=-1 || row.newRecord!=-1))) {
                 // only evaluate original values if different from current.
                if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
                    EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, cachedRows);
                }
                if (row.newRecord != -1) {
                    EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, cachedRows);
                }
                if (row.tempRecord != -1) {
                    EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, cachedRows);
                }
                return;
            }
            else if ((action == DataRowAction.Delete || (action==DataRowAction.Rollback && row.oldRecord==-1 && row.newRecord==-1)) && dependentColumns != null) {
                foreach(DataColumn col in dependentColumns) {                    
                    if (col.DataExpression != null && col.DataExpression.HasLocalAggregate() && col.Table == this) {
                        for (int j = 0; j < Rows.Count; j++) {
                            DataRow tableRow = Rows[j];
 
                            if (tableRow.oldRecord != -1 && tableRow.oldRecord != tableRow.newRecord) {
                                EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Original, null);
                            }                          
                        }
                        for (int j = 0; j < Rows.Count; j++)
                        {
                            DataRow tableRow = Rows[j];
                         
                            if (tableRow.tempRecord != -1)
                            {
                                EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Proposed, null);
                            }                           
                        }
                        // VSTFDEVDIV911434: Order is important here - we need to update proposed before current
                        // Oherwise rows that are in edit state will get ListChanged/PropertyChanged event before default value is changed
                        // It is also the reason why we are not doping it in the single loop: EvaluateDependentExpression can update the
                        // whole table, if it happens, current for all but first row is updated before proposed value
                        for (int j = 0; j < Rows.Count; j++)
                        {
                            DataRow tableRow = Rows[j];
                                                     
                            if (tableRow.newRecord != -1)
                            {
                                EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Current, null);
                            }
                        }
                        break;
                    }
                }
 
                if (cachedRows != null) {
                    foreach (DataRow relatedRow in cachedRows) {
                        if (relatedRow.oldRecord != -1 && relatedRow.oldRecord != relatedRow.newRecord) {
                            relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Original, null);
                        }
                        if (relatedRow.newRecord != -1) {
                            relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Current, null);
                        }
                        if (relatedRow.tempRecord != -1) {
                            relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Proposed, null);
                        }
                    }
                }
            }
        }
 
        internal void EvaluateExpressions(DataColumn column) {
            // evaluates all rows for expression from specified column
            Debug.Assert(column.Computed, "Only computed columns should be re-evaluated.");
            int count = column.table.Rows.Count;
            if (column.DataExpression.IsTableAggregate() && count > 0) {
                // this value is a constant across the table.
                object aggCurrent = column.DataExpression.Evaluate();
                for (int j = 0; j < count; j++) {
                    DataRow row = column.table.Rows[j];
                    // only evaluate original values if different from current.
                    if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
                        column[row.oldRecord] = aggCurrent;
                    }
                    if (row.newRecord != -1) {
                        column[row.newRecord] = aggCurrent;
                    }
                    if (row.tempRecord != -1) {
                        column[row.tempRecord] = aggCurrent;
                    }
                }
            }
            else {
                for (int j = 0; j < count; j++) {
                    DataRow row = column.table.Rows[j];
 
                    if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
                        column[row.oldRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Original);
                    }
                    if (row.newRecord != -1) {
                        column[row.newRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Current);
                    }
                    if (row.tempRecord != -1) {
                        column[row.tempRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Proposed);
                    }
                }
            }
 
            // SQLBU 501916 - DataTable internal index is corrupted:'5'
            column.Table.ResetInternalIndexes(column);
            EvaluateDependentExpressions(column);
        }
 
        internal void EvaluateDependentExpressions(DataColumn column) {
            // DataTable.Clear(), DataRowCollection.Clear() & DataColumn.set_Expression
            if (column.dependentColumns != null) {
                foreach (DataColumn dc in column.dependentColumns) {
                    if ((dc.table != null) && !Object.ReferenceEquals(column, dc)) { // SQLBU 502736
                        EvaluateExpressions(dc);
                    }
                }
            }
        }
 
        internal void EvaluateDependentExpressions(List<DataColumn> columns, DataRow row, DataRowVersion version, List<DataRow> cachedRows) {
            if (columns == null)
                return;
            //Expression evaluation is done first over same table expressions.
            int count = columns.Count;
            for(int i = 0; i < count; i++) {
                if (columns[i].Table == this) {// if this column is in my table
                    DataColumn dc = columns[i];
                    if (dc.DataExpression != null && dc.DataExpression.HasLocalAggregate()) {
                    // if column expression references a local Table aggregate we need to recalc it for the each row in the local table
                        DataRowVersion expressionVersion  = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
                        bool isConst = dc.DataExpression.IsTableAggregate(); //is expression constant for entire table?
                        object newValue = null;
                        if (isConst) {  //if new value, just compute once
                            newValue = dc.DataExpression.Evaluate(row, expressionVersion);
                        }
                        for (int j = 0; j < Rows.Count; j++) { //evaluate for all rows in the table
                            DataRow dr = Rows[j];
                            if (dr.RowState == DataRowState.Deleted) {
                                continue;
                            }
                            else if (expressionVersion == DataRowVersion.Original && (dr.oldRecord == -1 || dr.oldRecord == dr.newRecord)) {
                                continue;
                            }
 
                            if (!isConst) {
                                newValue = dc.DataExpression.Evaluate(dr, expressionVersion);
                            }
                            SilentlySetValue(dr, dc, expressionVersion, newValue);
                        }
                    }
                    else {
                        if (row.RowState == DataRowState.Deleted) {
                            continue;
                        }
                        else if (version == DataRowVersion.Original && (row.oldRecord == -1 || row.oldRecord == row.newRecord)) {
                            continue;
                        }
                        SilentlySetValue(row, dc, version, dc.DataExpression == null ? dc.DefaultValue : dc.DataExpression.Evaluate(row, version));
                    }
                }
            }
            // now do expression evaluation for expression columns other tables.
            count = columns.Count;
            for(int i = 0; i < count; i++) {
                DataColumn dc = columns[i];
                // if this column is NOT in my table or it is in the table and is not a local aggregate (self refs)
                if (dc.Table != this || (dc.DataExpression != null && !dc.DataExpression.HasLocalAggregate())) {
                    DataRowVersion foreignVer = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
 
                    // first - evaluate expressions for cachedRows (deletes & updates)
                    if (cachedRows != null) {
                        foreach (DataRow cachedRow in cachedRows) {
                            if (cachedRow.Table != dc.Table)
                                continue;
                             // don't update original version if child row doesn't have an oldRecord.
                            if (foreignVer == DataRowVersion.Original && cachedRow.newRecord == cachedRow.oldRecord)
                                 continue;
                            if (cachedRow != null && ((cachedRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || cachedRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
                                object newValue = dc.DataExpression.Evaluate(cachedRow, foreignVer);
                                SilentlySetValue(cachedRow, dc, foreignVer, newValue);
                            }
                        }
                    }
 
                    // next check parent relations
                    for (int j = 0; j < ParentRelations.Count; j++) {
                        DataRelation relation = ParentRelations[j];
                        if (relation.ParentTable != dc.Table)
                            continue;
                        foreach (DataRow parentRow in row.GetParentRows(relation, version)) {
                            if (cachedRows != null && cachedRows.Contains(parentRow))
                                continue;
                             // don't update original version if child row doesn't have an oldRecord.
                            if (foreignVer == DataRowVersion.Original && parentRow.newRecord == parentRow.oldRecord)
                                 continue;
                            if (parentRow != null && ((parentRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || parentRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
                                object newValue = dc.DataExpression.Evaluate(parentRow, foreignVer);
                                SilentlySetValue(parentRow, dc, foreignVer, newValue);
                            }
                        }
                    }
                    // next check child relations
                    for (int j = 0; j < ChildRelations.Count; j++) {
                        DataRelation relation = ChildRelations[j];
                        if (relation.ChildTable != dc.Table)
                            continue;
                        foreach (DataRow childRow in row.GetChildRows(relation, version)) {
                            // don't update original version if child row doesn't have an oldRecord.
                            if (cachedRows != null && cachedRows.Contains(childRow))
                                continue;
                            if (foreignVer == DataRowVersion.Original && childRow.newRecord == childRow.oldRecord)
                                continue;
                            if (childRow != null && ((childRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || childRow.oldRecord != -1))) { // if deleted GetRecordFromVersion will throw
                                object newValue = dc.DataExpression.Evaluate(childRow, foreignVer);
                                SilentlySetValue(childRow, dc, foreignVer, newValue);
                            }
                        }
                    }
                }
            }
        }
    }
}