CSharpDB.Engine
2.8.0
Prefix Reserved
See the version list below for details.
dotnet add package CSharpDB.Engine --version 2.8.0
NuGet\Install-Package CSharpDB.Engine -Version 2.8.0
<PackageReference Include="CSharpDB.Engine" Version="2.8.0" />
<PackageVersion Include="CSharpDB.Engine" Version="2.8.0" />
<PackageReference Include="CSharpDB.Engine" />
paket add CSharpDB.Engine --version 2.8.0
#r "nuget: CSharpDB.Engine, 2.8.0"
#:package CSharpDB.Engine@2.8.0
#addin nuget:?package=CSharpDB.Engine&version=2.8.0
#tool nuget:?package=CSharpDB.Engine&version=2.8.0
CSharpDB.Engine
Lightweight embedded SQL database engine for .NET with single-file storage, WAL durability, concurrent readers, and a typed Collection<T> NoSQL API.
Overview
CSharpDB.Engine is the main entry point for embedding CSharpDB in your .NET application. It combines the SQL parser, query planner, and B+tree storage engine into a single Database class with two access paths: a full SQL engine and a zero-SQL Collection<T> document API. You can run against a normal on-disk database file, open the engine fully in memory and explicitly save/load snapshots when needed, or use the hybrid lazy-resident mode that warms pages into memory on demand and keeps committed state durable on disk.
Features
- SQL engine: DDL, DML, JOINs, aggregates, GROUP BY, HAVING, CTEs,
UNION/INTERSECT/EXCEPT, scalar subqueries,IN (SELECT ...),EXISTS (SELECT ...), views, triggers, indexes,ANALYZE, andsys.*catalogs includingsys.table_statsandsys.column_stats - NoSQL Collection API: Typed
Collection<T>withPut/Get/Delete/Scan/Find - Single-file storage: All data in one
.dbfile with 4 KB B+tree pages - In-memory mode: Open empty in memory, load an existing
.db+.walinto memory, then save back to disk - Hybrid mode: Open lazily from disk, keep touched pages resident in process memory, and persist commits durably through the backing-file WAL
- WAL durability: Write-ahead log with crash recovery
- Concurrent readers: Snapshot-isolated readers alongside a single writer
- Statement + plan caching: bounded caches for parsed SQL statements and SELECT plan reuse
- Fast-path lookups: Direct B+tree access for
SELECT ... WHERE pk = value - Persisted statistics: Exact row counts maintained on write,
ANALYZE-refreshed column distinct/min/max stats, stale tracking after writes, and reuse of fresh stats forCOUNT(*), selective lookup planning, join method choice, and limited inner-join reordering - Async-first: All APIs are
async/awaitfrom top to bottom
Current boundary:
- Correlated subqueries are supported in
WHERE, non-aggregate projection expressions, andUPDATE/DELETEexpressions. - Correlated subqueries in
JOIN ON,GROUP BY,HAVING,ORDER BY, and aggregate projections remain unsupported. UNION ALLremains planned.
Usage
SQL API
using CSharpDB.Engine;
// Open or create a database
await using var db = await Database.OpenAsync("myapp.db");
// Create a table
await db.ExecuteAsync("""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT
)
""");
// Insert data
await db.ExecuteAsync("INSERT INTO users VALUES (1, 'Alice', 'alice@example.com')");
// Query data
var result = await db.ExecuteAsync("SELECT name, email FROM users WHERE id = 1");
while (await result.MoveNextAsync())
{
Console.WriteLine($"{result.Current[0].AsText} - {result.Current[1].AsText}");
}
// Transactions
await db.BeginTransactionAsync();
await db.ExecuteAsync("INSERT INTO users VALUES (2, 'Bob', 'bob@example.com')");
await db.CommitAsync();
In-Memory Open, Load, and Save
using CSharpDB.Engine;
// Start with an empty in-memory database
await using var db = await Database.OpenInMemoryAsync();
await db.ExecuteAsync("CREATE TABLE cache (id INTEGER PRIMARY KEY, value TEXT)");
await db.ExecuteAsync("INSERT INTO cache VALUES (1, 'hot data')");
// Persist the current committed state to disk
await db.SaveToFileAsync("cache.db");
// Load an existing on-disk database into memory, including committed WAL state
await using var imported = await Database.LoadIntoMemoryAsync("cache.db");
Hybrid Memory-Resident Mode
using CSharpDB.Engine;
// Start with an empty in-memory database and create a table
await using var db = await Database.OpenInMemoryAsync();
await db.ExecuteAsync("CREATE TABLE cache (id INTEGER PRIMARY KEY, value TEXT)");
// Persist the current committed state to disk
await db.SaveToFileAsync("cache.db");
await using var cacheDb = await Database.OpenHybridAsync(
"cache.db",
new DatabaseOptions(),
new HybridDatabaseOptions
{
PersistenceMode = HybridPersistenceMode.IncrementalDurable,
HotTableNames = ["cache"]
});
await cacheDb.ExecuteAsync("INSERT INTO cache VALUES (1, 'hot data')");
OpenHybridAsync(...) opens from the backing file lazily, keeps owned pages
resident in the pager cache after they are first touched, writes committed
changes durably through the backing-file WAL, and checkpoints those committed
pages into the base file over time.
Use HotTableNames and HotCollectionNames when a long-lived hybrid process
should preload selected read-mostly objects into the shared pager cache at
open. In v1:
- hot SQL tables warm the primary table B+tree plus SQL secondary indexes
- hot collections warm the backing
_col_...table only - hot-set warming is supported only for
IncrementalDurable - hot-set warming requires the default unbounded pager cache shape and is rejected for snapshot mode, bounded caches, and custom page-cache factories
If you want the older full-image export behavior, opt into snapshot mode:
await using var snapshotHybrid = await Database.OpenHybridAsync(
"cache.db",
new DatabaseOptions(),
new HybridDatabaseOptions
{
PersistenceMode = HybridPersistenceMode.Snapshot,
PersistenceTriggers = HybridPersistenceTriggers.Dispose
});
NoSQL Collection API
using CSharpDB.Engine;
await using var db = await Database.OpenAsync("myapp.db");
// Opens the existing "users" collection, or creates it if it doesn't exist yet.
var users = await db.GetCollectionAsync<User>("users");
// Put a document
await users.PutAsync("alice", new User
{
Name = "Alice",
Email = "alice@example.com",
Age = 30
});
// Get a document
var alice = await users.GetAsync("alice");
if (alice is not null)
{
Console.WriteLine($"{alice.Name} <{alice.Email}>");
}
// Scan all documents
await foreach (var entry in users.ScanAsync())
{
Console.WriteLine($"{entry.Key}: {entry.Value.Name}");
}
// Find with predicate
await foreach (var entry in users.FindAsync(u => u.Age >= 18))
{
Console.WriteLine($"Adult: {entry.Key} ({entry.Value.Name})");
}
public sealed class User
{
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public int Age { get; set; }
}
GetCollectionAsync<T>("users") is the create/open operation for collections. If
the collection does not exist yet, CSharpDB creates its backing storage
automatically the first time you call it.
Concurrent Readers
using CSharpDB.Engine;
await using var db = await Database.OpenAsync("myapp.db");
await db.ExecuteAsync("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
await db.ExecuteAsync("INSERT INTO users VALUES (1, 'Alice')");
// Take a snapshot-isolated reader session.
using var reader = db.CreateReaderSession();
// Writer continues changing the live database after the snapshot is created.
await db.ExecuteAsync("INSERT INTO users VALUES (2, 'Bob')");
// The reader still sees the earlier snapshot.
await using (var snapshotResult = await reader.ExecuteReadAsync(
"SELECT id, name FROM users ORDER BY id"))
{
while (await snapshotResult.MoveNextAsync())
{
Console.WriteLine(
$"{snapshotResult.Current[0].AsInteger}: {snapshotResult.Current[1].AsText}");
}
}
// The main database sees the latest committed state.
await using var liveResult = await db.ExecuteAsync("SELECT COUNT(*) FROM users");
await liveResult.MoveNextAsync();
Console.WriteLine($"Live row count: {liveResult.Current[0].AsInteger}");
ReaderSession gives you a stable snapshot from the moment it is created, even
while the writer keeps committing changes. Dispose each QueryResult before
executing the next query on the same reader session.
Reuse the same ReaderSession for a burst of related reads when possible. The
current file-backed tuning benchmarks show that reusing a snapshot is
materially cheaper than creating a new reader session for every single query.
Thread Safety
The supported threading model for Database is:
- Auto-commit writes can be issued concurrently against the same
DatabaseorCollection<T>, but they are serialized internally behind a single writer gate. - Only one explicit transaction can be active per
Database. Do not share one explicit transaction concurrently across multiple tasks. - Use one
ReaderSessionper concurrent SQL reader when you want snapshot-isolated reads alongside writes. - A single
ReaderSessionis not re-entrant and supports only one active query at a time. - The collection API does not yet expose its own snapshot-reader abstraction. For repeatable concurrent read isolation during writes, prefer SQL reads through
ReaderSession.
Installation
dotnet add package CSharpDB.Engine
For the recommended all-in-one package:
dotnet add package CSharpDB
Dependencies
CSharpDB.Primitives- shared type systemCSharpDB.Sql- SQL parserCSharpDB.Storage- B+tree storage engineCSharpDB.Execution- query planner and operators
Related Packages
| Package | Description |
|---|---|
| CSharpDB.Data | ADO.NET provider built on this engine |
| CSharpDB.Client | Authoritative client SDK over direct and remote transports |
| CSharpDB.Storage.Diagnostics | Storage inspection and integrity checking |
License
MIT - see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- CSharpDB.Execution (>= 2.8.0)
- CSharpDB.Primitives (>= 2.8.0)
- CSharpDB.Sql (>= 2.8.0)
- CSharpDB.Storage (>= 2.8.0)
- CSharpDB.Storage.Diagnostics (>= 2.8.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on CSharpDB.Engine:
| Package | Downloads |
|---|---|
|
CSharpDB.Data
ADO.NET provider for CSharpDB. Standard DbConnection, DbCommand, and DbDataReader with parameterized queries and transactions. |
|
|
CSharpDB.Client
Unified CSharpDB client SDK with pluggable transports (Direct, HTTP, gRPC, TCP, Named Pipes). |
|
|
CSharpDB
All-in-one package for CSharpDB application development. Includes the unified client, engine, ADO.NET provider, and diagnostics. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 91 | 4/8/2026 |
| 2.9.1 | 109 | 4/7/2026 |
| 2.8.1 | 106 | 4/6/2026 |
| 2.8.0 | 97 | 4/4/2026 |
| 2.7.0 | 98 | 3/31/2026 |
| 2.6.0 | 100 | 3/29/2026 |
| 2.5.0 | 202 | 3/28/2026 |
| 2.4.0 | 100 | 3/24/2026 |
| 2.3.0 | 99 | 3/22/2026 |
| 2.2.0 | 94 | 3/21/2026 |
| 2.0.1 | 114 | 3/14/2026 |
| 2.0.0 | 97 | 3/13/2026 |
| 1.9.0 | 119 | 3/12/2026 |
| 1.8.0 | 118 | 3/11/2026 |
| 1.7.0 | 118 | 3/8/2026 |
| 1.6.0 | 105 | 3/8/2026 |
| 1.5.0 | 107 | 3/7/2026 |
| 1.4.0 | 106 | 3/7/2026 |
| 1.3.0 | 106 | 3/6/2026 |
| 1.2.0 | 106 | 3/5/2026 |