PURE MANAGED .NET STORAGE ENGINE FOR ACCESS

Read & Write .mdb and .accdb Databases Anywhere.

JetDatabaseWriter is a 100% managed C# library for creating, querying, and updating Microsoft Access (Jet3, Jet4, and ACE) databases directly from files or streams — with zero OleDB, ODBC, COM, or native driver dependencies.

$dotnet add package JetDatabaseWriter
Explore C# Examples Reading Guide Writing Guide Support Contract
net11.0 & netstandard2.1 Windows · Linux · macOS Trimmed & NativeAOT Dafny-Verified Recovery & Codec Kernels
ACE .ACCDB · 4096B JET4 .MDB · UTF-16 JET3 .MDB · 2048B 0x01 · DATA & TDEF PAGES Slotted rows · OMAP bitmaps LVAL inline & multi-page chains 0x04 · B+TREE INDEX NODES Gen97 · GenLegacy · General Prefix-compressed branch & leaf CRYPTO · RC4 & AGILE AES AES-128/192/256 · CBC & CFB8 CryptoAPI & Standard AES DAFNY-VERIFIED KERNELS + ASYNC-FIRST .NET API Stream <-> CLOCK Cache <-> .jdw-journal Undo <-> IQueryable<T> Zero native DLLs · Checked arithmetic · Native DAO 3.6 & 12.0 verified
DIRECT PAGE-LEVEL STORAGE ENGINE

Everything Needed to Read, Query, and Author Access Databases

Built from the ground up in pure C# to manipulate JET and ACE page frames, catalogs, B+Tree indexes, and complex columns directly.

Pure Managed .NET

Targets net11.0 and netstandard2.1 (.NET Core 3.x and .NET 5–11). Runs on Windows, Linux, and macOS with zero P/Invoke, COM, OleDB, or ACE redistributable drivers.

Jet3, Jet4 & ACE Formats

Read, write, and create Access 97 (Jet3Mdb), Access 2000–2003 (Jet4Mdb), and Access 2007+ (AceAccdb) files from disk paths or any seekable Stream.

POCO Mapping & Async LINQ

Stream rows with Rows<T>, seek B+Tree indexes via FromIndex<T>, or compose Query<T> (IQueryable<T>) with automatic index selection and Include / ThenInclude.

Full Schema & Row Writer

Create, rename, and drop tables, columns, and indexes; enforce primary and foreign keys with cascading updates and deletes; evaluate defaults, validation rules, and calculated fields.

Built-In Encryption & Maintenance

Read and update Jet3/Jet4 RC4, ACE CryptoAPI, Standard AES, and Agile AES-128/192/256 databases. Encrypt, decrypt, and rotate passwords with atomic file replacement.

Transactions & Crash Recovery

Group or nest mutations with CommitAsync and RollbackAsync. Adjacent .jdw-journal rollback journals and Dafny-verified kernels recover interrupted file transactions on reopen.

Clean, Async-First C# APIs

Switch between reading, LINQ querying, schema authoring, transactions, encryption, and CLI model scaffolding.

using JetDatabaseWriter;

await using var reader = await AccessReader.OpenAsync("Northwind.mdb");

// List user tables in the catalog
var tables = await reader.ListTablesAsync();

// Stream strongly-typed POCOs lazily with automatic index inference
await foreach (var order in reader.Rows<Order>("Orders", o => o.CustomerId == 42))
{
    Console.WriteLine($"Order #{order.OrderID}: {order.Freight:C}");
}

public sealed class Order
{
    public int OrderID { get; set; }
    public int CustomerId { get; set; }
    public decimal Freight { get; set; }
}

Streaming & POCO Projection

Properties match column names case-insensitively or via [Column("...")] attributes.

  • Narrow projections decode only bound columns and skip unbound long-value chains.
  • Rows<T> streams over IAsyncEnumerable<T> with bounded memory.
  • FromIndex<T> offers explicit B+Tree range and equality seeks.
using JetDatabaseWriter;
using JetDatabaseWriter.Linq;

await using var reader = await AccessReader.OpenAsync("Northwind.accdb");

var recentOrders = await reader.Query<Order>("Orders")
    .Include(o => o.Customer)
    .ThenInclude(c => c!.Region)
    .Where(o => o.Freight >= 50m)
    .OrderByDescending(o => o.OrderID)
    .Take(25)
    .ToListAsync();

Async IQueryable<T> Provider

Compose queries using familiar LINQ operators with relationship-inferred eager loading.

  • Automatically selects covering unique/non-null B+Tree indexes for Where, OrderBy, MinAsync, and MaxAsync.
  • Include and ThenInclude resolve foreign keys declared in MSysRelationships.
  • Async terminals: ToListAsync, FirstOrDefaultAsync, CountAsync, AnyAsync.
using JetDatabaseWriter;
using JetDatabaseWriter.Enums;
using JetDatabaseWriter.Models;

await using var writer = await AccessWriter.CreateDatabaseAsync(
    "contacts.accdb", DatabaseFormat.AceAccdb);

await writer.CreateTableAsync("Contacts", new[]
{
    new ColumnDefinition("Id", typeof(int)) { IsPrimaryKey = true, IsAutoNumber = true },
    new ColumnDefinition("Email", typeof(string), maxLength: 120) { IsRequired = true },
    new ColumnDefinition("CreatedUtc", typeof(DateTime)) { DefaultValue = "=Now()" },
}, new[]
{
    new IndexDefinition("IX_Contacts_Email", "Email") { IsUnique = true },
});

await writer.InsertRowAsync("Contacts", new RowValues
{
    ["Email"] = "alice@example.com",
});

Authoring & Schema Evolution

Create databases from scratch or open existing files with AccessWriter.OpenAsync.

  • Maintains B+Tree indexes, usage maps, AutoNumber seeds, and foreign-key cascades.
  • Supports complex columns (Attachments, Multi-Value fields, Memo version history) and ACCDB calculated columns.
  • Oversized values refuse cleanly before mutating storage.
using JetDatabaseWriter;
using JetDatabaseWriter.Models;

await using var writer = await AccessWriter.OpenAsync("ledger.accdb");
await using var tx = await writer.BeginTransactionAsync();

await writer.UpdateRowsAsync(
    "Accounts",
    RowCriteria.Where("AccountId", 101),
    new RowValues { ["Balance"] = 950.00m });

await writer.InsertRowAsync("AuditLog", new RowValues
{
    ["AccountId"] = 101,
    ["Delta"] = -50.00m,
});

await tx.CommitAsync();

Atomic Statements & Crash Recovery

Group multiple row and schema changes into atomic transactions with nested savepoints.

  • File-backed writers record pre-images in an adjacent .jdw-journal undo journal.
  • If a process crashes mid-transaction, reopening the file with AccessWriter replays verified undo records automatically.
  • Caller streams support full in-process live rollback.
using JetDatabaseWriter;

// Open an encrypted database
await using var reader = await AccessReader.OpenAsync(
    "protected.accdb",
    new AccessReaderOptions("correct-horse-battery-staple"));

// Encrypt, decrypt, or rotate passwords in-place via atomic staging
await AccessDatabaseEncryption.ChangePasswordAsync(
    "protected.accdb",
    oldPassword: "correct-horse-battery-staple",
    newPassword: "new-strong-passphrase");

Multi-Generation Cryptography

Read, update, and maintain encrypted Access databases using managed .NET cryptography.

  • ACE Agile AES: AES-128/192/256 (CBC & CFB8) with SHA-1/256/384/512; creates & re-encrypts with AES-256-CBC / SHA-512.
  • ACE Standard & CryptoAPI: Read and update native pages.
  • Jet3 & Jet4 RC4: Full password & page encryption, including security SID/Owner remasking.
# Generate C# entity classes and relationship navigations from an existing file
dotnet run --project src/JetDatabaseWriter.Scaffold -- \
  --database Northwind.accdb \
  --output ./Models \
  --namespace MyApp.Data \
  --records \
  --nullable

Entity Model Scaffolding CLI

Inspect any .mdb or .accdb catalog and emit ready-to-compile C# models.

  • Generates classes or records with XML comments showing native JET column types and byte sizes.
  • Emits [Table] and [Column] attributes automatically when identifiers contain spaces or symbols.
  • Infers parent/child navigation properties from MSysRelationships for Query<T>.Include(...).

Supported Access Generations & On-Disk Formats

Interoperability is continuously verified against Microsoft-authored fixtures, DAO 3.6, and pinned ACEDAO 12.0.

Capability Jet3 (.mdb · Access 97) Jet4 (.mdb · Access 2000–2003) ACE (.accdb · Access 2007+)
Page Size & Text Encoding 2,048 bytes · Database Code Page 4,096 bytes · UTF-16LE + Compression 4,096 bytes · UTF-16LE + Compression
Create / Read / Write / Schema Supported Supported Supported
B+Tree Text Collations General97 GeneralLegacy · General General · GeneralLegacy
Encryption & Passwords Jet3 RC4 & Header Password Jet4 RC4, Header Password & SID Remasking Agile AES-128/192/256, Standard AES & CryptoAPI RC4
Complex Columns (Attachments / MVF) N/A in format N/A in format Read, Add, Replace, Delete & Memo History
Calculated Columns N/A in format N/A in format Native Expressions, Recompute & Regional Profiles

Formal Verification, Native DAO Gate & Fuzzing

Storage engines demand strict invariants. JetDatabaseWriter combines mathematical proof models, native Microsoft Access runtime validation, and adversarial fuzzing.

Dafny-Verified Recovery & Codec Kernels

Production recovery and value parsing execute reviewed C# generated from verified Dafny specifications covering bounded journal framing, replay identity, replacement field validation, byte authorization, long-value descriptors, data-page row mutations, index-entry ordering/leaf-chain admission, and usage-map bitmaps. No solver or verifier runtime ships with the package.

Zero-Skip Hosted ACEDAO 12.0 & DAO 3.6 Validation

Every CI run provisions the pinned Microsoft Access Runtime (ACEDAO.DLL) and executes a zero-skip native compatibility suite across net11.0 and netstandard2.1, verifying that databases created or mutated by JetDatabaseWriter open, query, update, and pass native Compact & Repair in Microsoft Access.

Hostile-File Fuzzing & CVE Traceability

Parser bounds are hardened against cyclic page chains, out-of-range offsets, and malformed descriptors. Every historical JET/ACE vulnerability in the CVE vulnerability analysis is tracked against canonical [Trait("CveAnalogue", ...)] xUnit regressions and continuous mutation/truncation fuzzing.

Strict Static Analysis, Reproducible Builds & NativeAOT

Compiled with TreatWarningsAsErrors, AnalysisLevel latest-all, WarningLevel 9999, checked arithmetic, Roslynator, StyleCop, and InferSharp v1.5 memory-safety analysis. Trimmed and NativeAOT smoke consumers verify every build.