You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All notable changes to JetDatabaseReader are documented here.
This project follows Semantic Versioning.
[2.2.0] — 2026-04-01
⚠️ Breaking Changes
Area
Before
After
TableResult.Rows
List<List<string>> — string rows
List<object[]> — typed CLR rows
ReadTable(string, int)
Returned string rows in Rows
Now returns typed rows in Rows
ReadTable(string, int, bool)
Bool flag selecting typed vs string mode
Removed — use ReadTable (typed) or ReadTableAsStrings (strings)
ReadTableAsync(string, int, bool)
Async counterpart of removed overload
Removed
FirstTableResult
Extended TableResult
Now extends StringTableResult
✨ New Type: StringTableResult
Dedicated result class for string-mode reads, returned by ReadTableAsStrings. Mirrors TableResult but Rows is List<List<string>>.
Property
Type
Description
Headers
List<string>
Column names
Rows
List<List<string>>
String rows
Schema
List<TableColumn>
Per-column schema
TableName
string
Source table name
RowCount
int
Computed row count
ToDataTable()
DataTable
All columns typeof(string)
✨ New Methods
Method
Returns
Description
ReadTableAsStrings(string, int)
StringTableResult
Sampled string-mode read
ReadTableAsStringsAsync(string, int)
Task<StringTableResult>
Async variant
TableResult.ToDataTable()
DataTable
Convert typed result — column types from Schema
StringTableResult.ToDataTable()
DataTable
Convert string result — all columns typeof(string)
🔧 Improvements & Bug Fixes
ACCDB encryption false positive fixed — The Jet4 encryption flag at offset 0x62 is now only checked for ver == 1 (Access 2000–2003 .mdb). Access 2007+ format databases (ver >= 2) set unrelated bits at that offset and were incorrectly rejected with NotSupportedException. All .accdb files and .mdb files saved in Access 2007–2019 format are now readable.
Model files split — ColumnSizeUnit, ColumnSize, and TableColumn each have their own .cs file.
📦 Migration Guide
// ── ReadTable — Rows is now typed ─────────────────────────────────────// Before (v2.1): string rows in RowsTableResultr=reader.ReadTable("Orders",10);stringval=r.Rows[0][2];// List<List<string>>// After (v2.2): typed rows in RowsTableResultr=reader.ReadTable("Orders",10);objectval=r.Rows[0][2];// List<object[]>// ── ReadTableAsStrings — dedicated string API ─────────────────────────StringTableResultsr=reader.ReadTableAsStrings("Orders",10);stringval=sr.Rows[0][2];// List<List<string>>// ── bool overload removed ─────────────────────────────────────────────// Before ❌TableResultr=reader.ReadTable("Orders",10,typedValues:true);// After ✅TableResultr=reader.ReadTable("Orders",10);StringTableResultsr=reader.ReadTableAsStrings("Orders",10);// ── ToDataTable ───────────────────────────────────────────────────────DataTabletyped=reader.ReadTable("Orders",100).ToDataTable();DataTablestrings=reader.ReadTableAsStrings("Orders",100).ToDataTable();
[2.1.0] — 2026-03-30
⚠️ Breaking Changes
Area
Before
After
TableColumn.TypeName
string — e.g. "Long Integer"
Removed — replaced by Type (System.Type)
TableColumn.SizeDesc
string — e.g. "4 bytes"
Removed — replaced by Size (ColumnSize struct)
ReadFirstTable()
Returned TableResult
Now returns FirstTableResult (subclass of TableResult)
GetTableStats()
Returned List<(string Name, long RowCount, int ColumnCount)>
Now returns List<TableStat>
✨ New Types
Type
Description
ColumnSizeUnit
Enum: Bits, Bytes, Chars, Variable, Lval
ColumnSize
Readonly struct — Value (int?) + Unit (ColumnSizeUnit). Factory methods: FromBits, FromBytes, FromChars. Sentinels: Variable, Lval. ToString() produces a human-readable description.
FirstTableResult
Extends TableResult with TableCount (int) — the total number of user tables in the database. Returned by ReadFirstTable().
TableStat
Named class with Name (string), RowCount (long), ColumnCount (int). Returned as element of List<TableStat> from GetTableStats().
🔧 Improvements
TableResult gains TableName (string) — the table this result was read from — and RowCount (int) computed property.
TableColumn.Type (System.Type) — exact CLR type, consistent with ColumnMetadata.ClrType.
TableColumn.Size (ColumnSize) — structured size with programmatic access to numeric value and unit; ToString() preserves the previous human-readable output.
📦 Migration Guide
// ── TableColumn schema properties ────────────────────────────────────// BeforestringtypeName=col.TypeName;// "Long Integer"stringsizeDesc=col.SizeDesc;// "4 bytes"// AfterTypeclrType=col.Type;// typeof(int)int?bytes=col.Size.Value;// 4stringdisplay=col.Size.ToString();// "4 bytes"boolisVar=col.Size.Unit==ColumnSizeUnit.Variable;// ── ReadFirstTable ────────────────────────────────────────────────────// BeforeTableResultr=reader.ReadFirstTable();// AfterFirstTableResultr=reader.ReadFirstTable();inttotal=r.TableCount;// new property on FirstTableResult// ── GetTableStats ─────────────────────────────────────────────────────// Before — tuple listforeach(var(name,rows,cols)inreader.GetTableStats()){ ...}// After — named classforeach(TableStatsinreader.GetTableStats())Console.WriteLine($"{s.Name}: {s.RowCount} rows, {s.ColumnCount} cols");
FileShare default changed to FileShare.Read — other processes may read but not write while the database is open; pass FileShare.ReadWrite explicitly when Microsoft Access has the file open
LRU page cache (256-page default, ~1 MB for Jet4 pages)