/// <summary>
        ///
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="headerRowIndex">
        ///     0-based index
        /// </param>
        /// <returns></returns>
        public static AdoTableReader ReadContiguousTableWithHeader(DataTable dataTable, int headerRowIndex)
        {
            Check.DoRequireArgumentNotNull(dataTable, nameof(dataTable));

            var headerRow   = dataTable.Rows[headerRowIndex];
            var firstColumn = dataTable.Columns.Cast <DataColumn>().FirstOrDefault(c => !AdoDataTableRowReader.IsNull(headerRow[c]));

            Check.DoCheckArgument(firstColumn != null, "Table header not found");

            Debug.Assert(firstColumn != null, nameof(firstColumn) + " != null");

            // last column could be same as start column
            var headerColumns = dataTable.Columns
                                .Cast <DataColumn>()
                                .Where(c => c.Ordinal >= firstColumn.Ordinal)
                                .TakeWhile(c => !AdoDataTableRowReader.IsNull(headerRow[c]))
                                .ToList();

            Check.DoCheckArgument(headerColumns.Count > 0, "No columns in header");

            Check.DoCheckArgument(headerColumns.All(c => headerRow[c] is string), "Header cells contain non-text values");

            var columns = headerColumns.Select(c => new KeyValuePair <string, int>((string)headerRow[c], c.Ordinal)).ToList();
            // two 1-based indexes
            var startRowIndexInclusive = headerRowIndex + 1;

            return(new AdoTableReader(dataTable, startRowIndexInclusive, null, columns));
        }
        private bool IsRowEmpty(int zeroBasedRowIndex)
        {
            var nonEmptyCellCount = _columnNameIndex.Count(p => !AdoDataTableRowReader.IsNull(DataTable.Rows[zeroBasedRowIndex][p.Value]));

            return(nonEmptyCellCount <= MaxNonEmptyCellInEmptyRow);
        }