The Embedded Database
Built for .NET
Zero dependencies. CSharpDB SQL. ACID storage. One NuGet package gives you everything from query parsing to page-level I/O.
dotnet add package CSharpDB
Ship in 30 Seconds
Use SQL, the Collection API, or low-level storage — all powered by one engine.
using CSharpDB.Data;
await using var conn = new CSharpDbConnection("Data Source=myapp.db");
await conn.OpenAsync();
await using var cmd = conn.CreateCommand();
cmd.CommandText = @"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY,
Name TEXT NOT NULL,
Email TEXT
)";
await cmd.ExecuteNonQueryAsync();
cmd.CommandText = "INSERT INTO Users VALUES (@id, @name, @email)";
cmd.Parameters.Add(new CSharpDbParameter("@id", 1));
cmd.Parameters.Add(new CSharpDbParameter("@name", "Alice"));
cmd.Parameters.Add(new CSharpDbParameter("@email", "alice@example.com"));
await cmd.ExecuteNonQueryAsync();
using CSharpDB.Engine;
await using var db = await Database.OpenAsync("myapp.db");
var users = await db.GetCollectionAsync<User>("users");
// Put and get by string key
await users.PutAsync("alice", new User("Alice", "alice@example.com", 30));
var alice = await users.GetAsync("alice");
// Create an index and query through it
await users.EnsureIndexAsync(u => u.Email);
await foreach (var match in users.FindByIndexAsync(
u => u.Email, "alice@example.com"))
Console.WriteLine(match.Value.Name);
public record User(string Name, string Email, int Age);
using System.Text;
using CSharpDB.Storage.BTrees;
using CSharpDB.Storage.StorageEngine;
var storageOptions = new StorageEngineOptionsBuilder()
.UseBTreeIndexes()
.Build();
var factory = new DefaultStorageEngineFactory();
var context = await factory.OpenAsync("lowlevel.cdb", storageOptions);
await using var pager = context.Pager;
await pager.BeginTransactionAsync();
try
{
uint rootPageId = await BTree.CreateNewAsync(pager);
var tree = new BTree(pager, rootPageId);
await tree.InsertAsync(42, Encoding.UTF8.GetBytes("session payload"));
byte[]? payload = await tree.FindAsync(42);
if (payload is not null)
Console.WriteLine(Encoding.UTF8.GetString(payload));
var cursor = tree.CreateCursor();
while (await cursor.MoveNextAsync())
Console.WriteLine($"{cursor.CurrentKey} = {cursor.CurrentValue.Length} bytes");
await tree.DeleteAsync(42);
await pager.CommitAsync();
}
catch
{
await pager.RollbackAsync();
throw;
}
Zero Dependencies
Pure .NET from page management to query execution. No native binaries, no external packages.
ACID Transactions
WAL-based recovery with snapshot isolation. Concurrent readers never block writers.
CSharpDB SQL Engine
A documented SQL subset with DDL, DML, JOINs, aggregates, CTEs, subqueries, views, triggers, and stored procedures. Read the SQL reference.
Collection API
Typed Collection<T> with JSON serialization, nested path indexing, and range queries.
B+Tree Storage
On-disk key-value storage with automatic node splitting, cursor iteration, and range scans.
ETL Pipelines
Built-in pipeline runtime with CSV/JSON sources, transforms, and destinations.
Every Tool You Need
One engine, many interfaces. Pick the access layer that fits your architecture.
CLI REPL
Interactive SQL shell with autocomplete and result formatting
Admin UI
Browser-based dashboard for browsing, querying, and managing data
REST API
HTTP endpoints for SQL execution, collections, and database management
gRPC Daemon
High-performance binary protocol for remote database access
MCP Server
Model Context Protocol server for AI agent database access
VS Code Extension
Browse databases, run queries, and inspect schemas from your editor
ADO.NET Provider
Standard DbConnection/DbCommand for ORM and data library compatibility
Native FFI
C-compatible shared library for Python, JavaScript, and other languages
Use from Any Language
Compile to a native shared library with NativeAOT. Call CSharpDB from Python, Rust, Go, Node.js, or anything that speaks C.
from csharpdb import CSharpDB
with CSharpDB() as db:
db.open("app.db")
print(db.query("SELECT 1 AS value"))
Requires the native library and the wrapper from the Python setup tutorial. Run from the folder containing the library, or pass its path to the wrapper.
import { CSharpDB } from './csharpdb.mjs';
const db = new CSharpDB();
try {
db.open('app.db');
console.log(db.query('SELECT 1 AS value'));
} finally {
db.close();
}
Requires the native library and the wrapper from the Node.js setup tutorial. Run from the folder containing the library, or pass its path to the wrapper.
Use the native header to define your bindings. csharpdb_open(path) returns a database handle; pass it to csharpdb_execute(db, sql) and csharpdb_close(db). Check failed calls with csharpdb_last_error() and free each result with csharpdb_result_free(result).
Read the C ABI header and build the native library. Rust bindings and linker configuration are supplied by your application.
Use the native header to define your bindings. csharpdb_open(path) returns a database handle; pass it to csharpdb_execute(db, sql) and csharpdb_close(db). Check failed calls with csharpdb_last_error() and free each result with csharpdb_result_free(result).
Read the C ABI header and build the native library. Go bindings and linker configuration are supplied by your application.
Build the native project explicitly, for example: dotnet publish src/CSharpDB.Native/CSharpDB.Native.csproj -c Release -r linux-x64. The output is CSharpDB.Native.so on Linux, CSharpDB.Native.dll on Windows, or CSharpDB.Native.dylib on macOS. See the platform prerequisites.
Built for .NET
Not a port. Not a wrapper. Native .NET from the ground up.
NativeAOT Ready
The native shared library is published with NativeAOT. For embedded applications, use the SQL API or registered generated collection models and review the trimming requirements for your chosen API.
Zero-Alloc Hot Paths
Critical read paths use Span<T> and stack allocation. No GC pressure where it matters most.
IAsyncDisposable
First-class await using support everywhere. Clean resource lifetime management with no surprises.
System.Threading.Channels
Internal write batching uses bounded channels for backpressure-aware, lock-free coordination.
Generated Collection Models
The regular GetCollectionAsync<T> API uses reflection-based serialization and member binding. Use GetGeneratedCollectionAsync<T> with a registered model for the trim-safe generated path. See collection API requirements.
How CSharpDB Compares
Compare the engine model and access APIs that fit your application.
| Area | CSharpDB | SQLite | LiteDB v5 | RocksDB |
|---|---|---|---|---|
| Data model | SQL tables and typed collections | SQL tables | Document collections | Ordered key-value pairs |
| Application access | Engine API, Collections, ADO.NET and EF Core | C API and language bindings | .NET collection API and object mapping | Key-value API and language bindings |
| Related data | SQL JOINs and subqueries | SQL JOINs and subqueries | Embedded documents and document references | Application-defined keys and values |
| String ordering | Collation support | Collating functions | Culture and comparison options | Configurable key comparator |
| Documentation | SQL support · Tools | SQLite features | LiteDB overview | RocksDB overview |
CSharpDB includes separate Admin, CLI, REST, gRPC, and MCP tools. Other databases have their own tooling and integrations; this overview does not rate the completeness of those ecosystems. For measured CSharpDB workloads and storage-mode tradeoffs, see the performance guide.