File: System\Data\DataTableReader.cs
Web Access
Project: src\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
 
namespace System.Data
{
    public sealed class DataTableReader : DbDataReader
    {
        private readonly DataTable[] _tables;
        private bool _isOpen = true;
        private DataTable? _schemaTable;
 
        private int _tableCounter = -1;
        private int _rowCounter = -1;
        private DataTable _currentDataTable = default!; // Always initialized in Init
        private DataRow? _currentDataRow;
 
        private bool _hasRows = true;
        private bool _reachEORows;
        private bool _currentRowRemoved;
        private bool _schemaIsChanged;
        private bool _started;
        private bool _readerIsInvalid;
        private DataTableReaderListener _listener = default!; // Always initialized in Init
        private bool _tableCleared;
 
        public DataTableReader(DataTable dataTable)
        {
            if (dataTable == null)
            {
                throw ExceptionBuilder.ArgumentNull(nameof(DataTable));
            }
            _tables = new DataTable[1] { dataTable };
 
            Init();
        }
 
        public DataTableReader(DataTable[] dataTables)
        {
            if (dataTables == null)
            {
                throw ExceptionBuilder.ArgumentNull(nameof(DataTable));
            }
            if (dataTables.Length == 0)
            {
                throw ExceptionBuilder.DataTableReaderArgumentIsEmpty();
            }
 
            _tables = new DataTable[dataTables.Length];
            for (int i = 0; i < dataTables.Length; i++)
            {
                if (dataTables[i] == null)
                {
                    throw ExceptionBuilder.ArgumentNull(nameof(DataTable));
                }
                _tables[i] = dataTables[i];
            }
 
            Init();
        }
 
        private bool ReaderIsInvalid
        {
            get { return _readerIsInvalid; }
            set
            {
                if (_readerIsInvalid == value)
                {
                    return;
                }
 
                _readerIsInvalid = value;
                if (_readerIsInvalid && _listener != null)
                {
                    _listener.CleanUp();
                }
            }
        }
 
        private bool IsSchemaChanged
        {
            get { return _schemaIsChanged; }
            set
            {
                if (!value || _schemaIsChanged == value) //once it is set to false; should not change unless in init() or NextResult()
                {
                    return;
                }
 
                _schemaIsChanged = value;
                _listener?.CleanUp();
            }
        }
 
        internal DataTable CurrentDataTable => _currentDataTable;
 
        private void Init()
        {
            _tableCounter = 0;
            _reachEORows = false;
            _schemaIsChanged = false;
            _currentDataTable = _tables[_tableCounter];
            _hasRows = (_currentDataTable.Rows.Count > 0);
            ReaderIsInvalid = false;
 
            // we need to listen to current tables event so create a listener, it will listen to events and call us back.
            _listener = new DataTableReaderListener(this);
        }
 
        public override void Close()
        {
            if (!_isOpen)
            {
                return;
            }
 
            // no need to listen to events after close
            _listener?.CleanUp();
 
            _listener = null!;
            _schemaTable = null;
            _isOpen = false;
        }
 
        public override DataTable GetSchemaTable()
        {
            ValidateOpen(nameof(GetSchemaTable));
            ValidateReader();
 
            // each time, we just get schema table of current table for once, no need to recreate each time, if schema is changed, reader is already
            // is invalid
            return _schemaTable ??= GetSchemaTableFromDataTable(_currentDataTable);
        }
 
        public override bool NextResult()
        {
            // next result set; reset everything
            ValidateOpen(nameof(NextResult));
 
            if ((_tableCounter == _tables.Length - 1))
            {
                return false;
            }
 
            _currentDataTable = _tables[++_tableCounter];
 
            _listener?.UpdataTable(_currentDataTable); // it will unsubscribe from preveous tables events and subscribe to new table's events
 
            _schemaTable = null;
            _rowCounter = -1;
            _currentRowRemoved = false;
            _reachEORows = false;
            _schemaIsChanged = false;
            _started = false;
            ReaderIsInvalid = false;
            _tableCleared = false;
 
            _hasRows = (_currentDataTable.Rows.Count > 0);
 
            return true;
        }
 
        public override bool Read()
        {
            if (!_started)
            {
                _started = true;
            }
 
            ValidateOpen(nameof(Read));
            ValidateReader();
 
            if (_reachEORows)
            {
                return false;
            }
 
            if (_rowCounter >= _currentDataTable.Rows.Count - 1)
            {
                _reachEORows = true;
                _listener?.CleanUp();
                return false;
            }
 
            _rowCounter++;
            ValidateRow(_rowCounter);
            _currentDataRow = _currentDataTable.Rows[_rowCounter];
 
            while (_currentDataRow.RowState == DataRowState.Deleted)
            {
                _rowCounter++;
                if (_rowCounter == _currentDataTable.Rows.Count)
                {
                    _reachEORows = true;
                    _listener?.CleanUp();
                    return false;
                }
                ValidateRow(_rowCounter);
                _currentDataRow = _currentDataTable.Rows[_rowCounter];
            }
            if (_currentRowRemoved)
            {
                _currentRowRemoved = false;
            }
 
            return true;
        }
 
        public override int Depth
        {
            get
            {
                ValidateOpen(nameof(Depth));
                ValidateReader();
                return 0;
            }
        }
 
        public override bool IsClosed => !_isOpen;
 
        public override int RecordsAffected
        {
            get
            {
                ValidateReader();
                return 0;
            }
        }
 
        public override bool HasRows
        {
            get
            {
                ValidateOpen(nameof(HasRows));
                ValidateReader();
                return _hasRows;
            }
        }
 
        public override object this[int ordinal]
        {
            get
            {
                ValidateOpen("Item");
                ValidateReader();
                if ((_currentDataRow == null) || (_currentDataRow.RowState == DataRowState.Deleted))
                {
                    ReaderIsInvalid = true;
                    throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
                }
                try
                {
                    return _currentDataRow[ordinal];
                }
                catch (IndexOutOfRangeException e)
                {
                    // thrown by DataColumnCollection
                    ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                    throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
                }
            }
        }
 
        public override object this[string name]
        {
            get
            {
                ValidateOpen("Item");
                ValidateReader();
                if ((_currentDataRow == null) || (_currentDataRow.RowState == DataRowState.Deleted))
                {
                    ReaderIsInvalid = true;
                    throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
                }
                return _currentDataRow[name];
            }
        }
 
        public override int FieldCount
        {
            get
            {
                ValidateOpen(nameof(FieldCount));
                ValidateReader();
                return _currentDataTable.Columns.Count;
            }
        }
 
        [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)]
        public override Type GetProviderSpecificFieldType(int ordinal)
        {
            ValidateOpen(nameof(GetProviderSpecificFieldType));
            ValidateReader();
            return GetFieldType(ordinal);
        }
 
        public override object GetProviderSpecificValue(int ordinal)
        {
            ValidateOpen(nameof(GetProviderSpecificValue));
            ValidateReader();
            return GetValue(ordinal);
        }
 
        public override int GetProviderSpecificValues(object[] values)
        {
            ValidateOpen(nameof(GetProviderSpecificValues));
            ValidateReader();
            return GetValues(values);
        }
 
        public override bool GetBoolean(int ordinal)
        {
            ValidateState(nameof(GetBoolean));
            ValidateReader();
            try
            {
                return (bool)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override byte GetByte(int ordinal)
        {
            ValidateState(nameof(GetByte));
            ValidateReader();
            try
            {
                return (byte)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override long GetBytes(int ordinal, long dataIndex, byte[]? buffer, int bufferIndex, int length)
        {
            ValidateState(nameof(GetBytes));
            ValidateReader();
            byte[] tempBuffer;
            try
            {
                tempBuffer = (byte[])_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
 
            if (buffer == null)
            {
                return tempBuffer.Length;
            }
 
            int srcIndex = (int)dataIndex;
            int byteCount = Math.Min(tempBuffer.Length - srcIndex, length);
            if (srcIndex < 0)
            {
                throw ADP.InvalidSourceBufferIndex(tempBuffer.Length, srcIndex, nameof(dataIndex));
            }
            else if ((bufferIndex < 0) || (bufferIndex > 0 && bufferIndex >= buffer.Length))
            {
                throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex));
            }
 
            if (0 < byteCount)
            {
                Array.Copy(tempBuffer, dataIndex, buffer, bufferIndex, byteCount);
            }
            else if (length < 0)
            {
                throw ADP.InvalidDataLength(length);
            }
            else
            {
                byteCount = 0;
            }
            return byteCount;
        }
 
        public override char GetChar(int ordinal)
        {
            ValidateState(nameof(GetChar));
            ValidateReader();
            try
            {
                return (char)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override long GetChars(int ordinal, long dataIndex, char[]? buffer, int bufferIndex, int length)
        {
            ValidateState(nameof(GetChars));
            ValidateReader();
            char[] tempBuffer;
            try
            {
                tempBuffer = (char[])_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
 
            if (buffer == null)
            {
                return tempBuffer.Length;
            }
 
            int srcIndex = (int)dataIndex;
            int charCount = Math.Min(tempBuffer.Length - srcIndex, length);
            if (srcIndex < 0)
            {
                throw ADP.InvalidSourceBufferIndex(tempBuffer.Length, srcIndex, nameof(dataIndex));
            }
            else if ((bufferIndex < 0) || (bufferIndex > 0 && bufferIndex >= buffer.Length))
            {
                throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, nameof(bufferIndex));
            }
 
            if (0 < charCount)
            {
                Array.Copy(tempBuffer, dataIndex, buffer, bufferIndex, charCount);
            }
            else if (length < 0)
            {
                throw ADP.InvalidDataLength(length);
            }
            else
            {
                charCount = 0;
            }
            return charCount;
        }
 
        public override string GetDataTypeName(int ordinal)
        {
            ValidateOpen(nameof(GetDataTypeName));
            ValidateReader();
            return GetFieldType(ordinal).Name;
        }
 
        public override DateTime GetDateTime(int ordinal)
        {
            ValidateState(nameof(GetDateTime));
            ValidateReader();
            try
            {
                return (DateTime)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override decimal GetDecimal(int ordinal)
        {
            ValidateState(nameof(GetDecimal));
            ValidateReader();
            try
            {
                return (decimal)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override double GetDouble(int ordinal)
        {
            ValidateState(nameof(GetDouble));
            ValidateReader();
            try
            {
                return (double)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            { // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)]
        public override Type GetFieldType(int ordinal)
        {
            ValidateOpen(nameof(GetFieldType));
            ValidateReader();
            try
            {
                return (_currentDataTable.Columns[ordinal].DataType);
            }
            catch (IndexOutOfRangeException e)
            { // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override float GetFloat(int ordinal)
        {
            ValidateState(nameof(GetFloat));
            ValidateReader();
            try
            {
                return (float)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override Guid GetGuid(int ordinal)
        {
            ValidateState(nameof(GetGuid));
            ValidateReader();
            try
            {
                return (Guid)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override short GetInt16(int ordinal)
        {
            ValidateState(nameof(GetInt16));
            ValidateReader();
            try
            {
                return (short)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override int GetInt32(int ordinal)
        {
            ValidateState(nameof(GetInt32));
            ValidateReader();
            try
            {
                return (int)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override long GetInt64(int ordinal)
        {
            ValidateState(nameof(GetInt64));
            ValidateReader();
            try
            {
                return (long)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override string GetName(int ordinal)
        {
            ValidateOpen(nameof(GetName));
            ValidateReader();
            try
            {
                return (_currentDataTable.Columns[ordinal].ColumnName);
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override int GetOrdinal(string name)
        {
            ValidateOpen(nameof(GetOrdinal));
            ValidateReader();
            DataColumn? dc = _currentDataTable.Columns[name];
 
            if (dc != null)
            {
                return dc.Ordinal;
            }
            else
            {
                throw ExceptionBuilder.ColumnNotInTheTable(name, _currentDataTable.TableName);
            }
        }
 
        public override string GetString(int ordinal)
        {
            ValidateState(nameof(GetString));
            ValidateReader();
            try
            {
                return (string)_currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override object GetValue(int ordinal)
        {
            ValidateState(nameof(GetValue));
            ValidateReader();
            try
            {
                return _currentDataRow![ordinal];
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override int GetValues(object[] values)
        {
            ValidateState(nameof(GetValues));
            ValidateReader();
 
            if (values == null)
            {
                throw ExceptionBuilder.ArgumentNull(nameof(values));
            }
 
            Array.Copy(_currentDataRow!.ItemArray, values, _currentDataRow.ItemArray.Length > values.Length ? values.Length : _currentDataRow.ItemArray.Length);
            return (_currentDataRow.ItemArray.Length > values.Length ? values.Length : _currentDataRow.ItemArray.Length);
        }
        public override bool IsDBNull(int ordinal)
        {
            ValidateState(nameof(IsDBNull));
            ValidateReader();
            try
            {
                return (_currentDataRow!.IsNull(ordinal));
            }
            catch (IndexOutOfRangeException e)
            {
                // thrown by DataColumnCollection
                ExceptionBuilder.TraceExceptionWithoutRethrow(e);
                throw ExceptionBuilder.ArgumentOutOfRange(nameof(ordinal));
            }
        }
 
        public override IEnumerator GetEnumerator()
        {
            ValidateOpen(nameof(GetEnumerator));
            return new DbEnumerator((IDataReader)this);
        }
 
        internal static DataTable GetSchemaTableFromDataTable(DataTable table)
        {
            if (table == null)
            {
                throw ExceptionBuilder.ArgumentNull(nameof(DataTable));
            }
 
            DataTable tempSchemaTable = new DataTable("SchemaTable");
            tempSchemaTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
 
            DataColumn ColumnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(string));
            DataColumn ColumnOrdinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(int));
            DataColumn ColumnSize = new DataColumn(SchemaTableColumn.ColumnSize, typeof(int));
            DataColumn NumericPrecision = new DataColumn(SchemaTableColumn.NumericPrecision, typeof(short));
            DataColumn NumericScale = new DataColumn(SchemaTableColumn.NumericScale, typeof(short));
            DataColumn DataType = GetSystemTypeDataColumn();
 
            [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers",
                Justification = "The problem is Type.TypeInitializer which requires constructors on the Type instance." +
                    "In this case the TypeInitializer property is not accessed dynamically.")]
            static DataColumn GetSystemTypeDataColumn() =>
                new DataColumn(SchemaTableColumn.DataType, typeof(Type));
 
            DataColumn ProviderType = new DataColumn(SchemaTableColumn.ProviderType, typeof(int));
            DataColumn IsLong = new DataColumn(SchemaTableColumn.IsLong, typeof(bool));
            DataColumn AllowDBNull = new DataColumn(SchemaTableColumn.AllowDBNull, typeof(bool));
            DataColumn IsReadOnly = new DataColumn(SchemaTableOptionalColumn.IsReadOnly, typeof(bool));
            DataColumn IsRowVersion = new DataColumn(SchemaTableOptionalColumn.IsRowVersion, typeof(bool));
            DataColumn IsUnique = new DataColumn(SchemaTableColumn.IsUnique, typeof(bool));
            DataColumn IsKeyColumn = new DataColumn(SchemaTableColumn.IsKey, typeof(bool));
            DataColumn IsAutoIncrement = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement, typeof(bool));
            DataColumn BaseSchemaName = new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(string));
            DataColumn BaseCatalogName = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName, typeof(string));
            DataColumn BaseTableName = new DataColumn(SchemaTableColumn.BaseTableName, typeof(string));
            DataColumn BaseColumnName = new DataColumn(SchemaTableColumn.BaseColumnName, typeof(string));
            DataColumn AutoIncrementSeed = new DataColumn(SchemaTableOptionalColumn.AutoIncrementSeed, typeof(long));
            DataColumn AutoIncrementStep = new DataColumn(SchemaTableOptionalColumn.AutoIncrementStep, typeof(long));
            DataColumn DefaultValue = new DataColumn(SchemaTableOptionalColumn.DefaultValue, typeof(object));
            DataColumn Expression = new DataColumn(SchemaTableOptionalColumn.Expression, typeof(string));
            DataColumn ColumnMapping = new DataColumn(SchemaTableOptionalColumn.ColumnMapping, typeof(MappingType));
            DataColumn BaseTableNamespace = new DataColumn(SchemaTableOptionalColumn.BaseTableNamespace, typeof(string));
            DataColumn BaseColumnNamespace = new DataColumn(SchemaTableOptionalColumn.BaseColumnNamespace, typeof(string));
 
            ColumnSize.DefaultValue = -1;
 
            if (table.DataSet != null)
            {
                BaseCatalogName.DefaultValue = table.DataSet.DataSetName;
            }
 
            BaseTableName.DefaultValue = table.TableName;
            BaseTableNamespace.DefaultValue = table.Namespace;
            IsRowVersion.DefaultValue = false;
            IsLong.DefaultValue = false;
            IsReadOnly.DefaultValue = false;
            IsKeyColumn.DefaultValue = false;
            IsAutoIncrement.DefaultValue = false;
            AutoIncrementSeed.DefaultValue = 0;
            AutoIncrementStep.DefaultValue = 1;
 
            tempSchemaTable.Columns.Add(ColumnName);
            tempSchemaTable.Columns.Add(ColumnOrdinal);
            tempSchemaTable.Columns.Add(ColumnSize);
            tempSchemaTable.Columns.Add(NumericPrecision);
            tempSchemaTable.Columns.Add(NumericScale);
            tempSchemaTable.Columns.Add(DataType);
            tempSchemaTable.Columns.Add(ProviderType);
            tempSchemaTable.Columns.Add(IsLong);
            tempSchemaTable.Columns.Add(AllowDBNull);
            tempSchemaTable.Columns.Add(IsReadOnly);
            tempSchemaTable.Columns.Add(IsRowVersion);
            tempSchemaTable.Columns.Add(IsUnique);
            tempSchemaTable.Columns.Add(IsKeyColumn);
            tempSchemaTable.Columns.Add(IsAutoIncrement);
            tempSchemaTable.Columns.Add(BaseCatalogName);
            tempSchemaTable.Columns.Add(BaseSchemaName);
 
            // specific to datatablereader
            tempSchemaTable.Columns.Add(BaseTableName);
            tempSchemaTable.Columns.Add(BaseColumnName);
            tempSchemaTable.Columns.Add(AutoIncrementSeed);
            tempSchemaTable.Columns.Add(AutoIncrementStep);
            tempSchemaTable.Columns.Add(DefaultValue);
            tempSchemaTable.Columns.Add(Expression);
            tempSchemaTable.Columns.Add(ColumnMapping);
            tempSchemaTable.Columns.Add(BaseTableNamespace);
            tempSchemaTable.Columns.Add(BaseColumnNamespace);
 
            foreach (DataColumn dc in table.Columns)
            {
                DataRow dr = tempSchemaTable.NewRow();
 
                dr[ColumnName] = dc.ColumnName;
                dr[ColumnOrdinal] = dc.Ordinal;
                dr[DataType] = dc.DataType;
 
                if (dc.DataType == typeof(string))
                {
                    dr[ColumnSize] = dc.MaxLength;
                }
 
                dr[AllowDBNull] = dc.AllowDBNull;
                dr[IsReadOnly] = dc.ReadOnly;
                dr[IsUnique] = dc.Unique;
 
                if (dc.AutoIncrement)
                {
                    dr[IsAutoIncrement] = true;
                    dr[AutoIncrementSeed] = dc.AutoIncrementSeed;
                    dr[AutoIncrementStep] = dc.AutoIncrementStep;
                }
 
                if (dc.DefaultValue != DBNull.Value)
                {
                    dr[DefaultValue] = dc.DefaultValue;
                }
 
                if (dc.Expression.Length != 0)
                {
                    bool hasExternalDependency = false;
                    DataColumn[] dependency = dc.DataExpression!.GetDependency();
                    for (int j = 0; j < dependency.Length; j++)
                    {
                        if (dependency[j].Table != table)
                        {
                            hasExternalDependency = true;
                            break;
                        }
                    }
                    if (!hasExternalDependency)
                    {
                        dr[Expression] = dc.Expression;
                    }
                }
 
                dr[ColumnMapping] = dc.ColumnMapping;
                dr[BaseColumnName] = dc.ColumnName;
                dr[BaseColumnNamespace] = dc.Namespace;
 
                tempSchemaTable.Rows.Add(dr);
            }
 
            foreach (DataColumn key in table.PrimaryKey)
            {
                tempSchemaTable.Rows[key.Ordinal][IsKeyColumn] = true;
            }
 
            tempSchemaTable.AcceptChanges();
 
            return tempSchemaTable;
        }
 
        private void ValidateOpen(string caller)
        {
            if (!_isOpen)
            {
                throw ADP.DataReaderClosed(caller);
            }
        }
 
        private void ValidateReader()
        {
            if (ReaderIsInvalid)
            {
                throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
            }
 
            if (IsSchemaChanged)
            {
                throw ExceptionBuilder.DataTableReaderSchemaIsInvalid(_currentDataTable.TableName); // may be we can use better error message!
            }
        }
 
        private void ValidateState(string caller)
        {
            ValidateOpen(caller);
            if (_tableCleared)
            {
                throw ExceptionBuilder.EmptyDataTableReader(_currentDataTable.TableName);
            }
 
            // see if without any event raising, if our current row has some changes!if so reader is invalid.
            if ((_currentDataRow == null) || (_currentDataTable == null))
            {
                ReaderIsInvalid = true;
                // TODO: This will throw an NRE
                throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable!.TableName);
            }
 
            //See if without any event raing, if our rows are deleted, or removed! Reader is not invalid, user should be able to read and reach goo row
            if ((_currentDataRow.RowState == DataRowState.Deleted) || (_currentDataRow.RowState == DataRowState.Detached) || _currentRowRemoved)
            {
                throw ExceptionBuilder.InvalidCurrentRowInDataTableReader();
            }
            // user may have called clear (which removes the rows without raing event) or deleted part of rows without raising event!if so reader is invalid.
            if (0 > _rowCounter || _currentDataTable.Rows.Count <= _rowCounter)
            {
                ReaderIsInvalid = true;
                throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
            }
        }
 
        private void ValidateRow(int rowPosition)
        {
            if (ReaderIsInvalid)
            {
                throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
            }
 
            if (0 > rowPosition || _currentDataTable.Rows.Count <= rowPosition)
            {
                ReaderIsInvalid = true;
                throw ExceptionBuilder.InvalidDataTableReader(_currentDataTable.TableName);
            }
        }
 
        // Event Call backs from DataTableReaderListener,  will invoke these methods
        internal void SchemaChanged()
        {
            IsSchemaChanged = true;
        }
 
        internal void DataTableCleared()
        {
            if (!_started)
            {
                return;
            }
 
            _rowCounter = -1;
            if (!_reachEORows)
            {
                _currentRowRemoved = true;
            }
        }
 
        internal void DataChanged(DataRowChangeEventArgs args)
        {
            if ((!_started) || (_rowCounter == -1 && !_tableCleared))
            {
                return;
            }
 
            switch (args.Action)
            {
                case DataRowAction.Add:
                    ValidateRow(_rowCounter + 1);
                    if (_currentDataRow == _currentDataTable.Rows[_rowCounter + 1])
                    {
                        // check if we moved one position up
                        _rowCounter++;  // if so, refresh the datarow and fix the counter
                    }
                    break;
                case DataRowAction.Delete: // delete
                case DataRowAction.Rollback:// rejectchanges
                case DataRowAction.Commit: // acceptchanges
                    if (args.Row.RowState == DataRowState.Detached)
                    {
                        if (args.Row != _currentDataRow)
                        {
                            if (_rowCounter == 0) // if I am at first row and no previous row exist,NOOP
                                break;
                            ValidateRow(_rowCounter - 1);
                            if (_currentDataRow == _currentDataTable.Rows[_rowCounter - 1])
                            {
                                // one of previous rows is detached, collection size is changed!
                                _rowCounter--;
                            }
                        }
                        else
                        { // we are processing current datarow
                            _currentRowRemoved = true;
                            if (_rowCounter > 0)
                            {
                                // go back one row, no matter what the state is
                                _rowCounter--;
                                _currentDataRow = _currentDataTable.Rows[_rowCounter];
                            }
                            else
                            {
                                // we are on 0th row, so reset data to initial state!
                                _rowCounter = -1;
                                _currentDataRow = null;
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }
}