// CSharpDB — Embedded Database Engine for .NET // Zero dependencies. ACID-compliant. Production-grade. using CSharpDB.Engine; using CSharpDB.Data; using System.Threading.Channels; namespace MyApp.DataLayer; public sealed class AppDatabase : IAsyncDisposable { private readonly Database _db; private readonly Channel<WriteOp> _writes; public static async Task<AppDatabase> OpenAsync(string path) { var db = await Database.OpenAsync(path); return new AppDatabase(db); } private AppDatabase(Database db) { _db = db; _writes = Channel.CreateBounded<WriteOp>(1024); } public async Task CreateSchemaAsync() { 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 Products ( Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Price REAL NOT NULL, Category TEXT, Created TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_products_category ON Products(Category); CREATE TABLE IF NOT EXISTS Orders ( Id INTEGER PRIMARY KEY, ProductId INTEGER REFERENCES Products(Id), Quantity INTEGER NOT NULL, Total REAL NOT NULL, OrderDate TEXT DEFAULT CURRENT_TIMESTAMP );"; await cmd.ExecuteNonQueryAsync(); } public async Task<IReadOnlyList<Product>> GetTopProductsAsync(int limit) { await using var conn = new CSharpDbConnection("Data Source=myapp.db"); await conn.OpenAsync(); await using var cmd = conn.CreateCommand(); cmd.CommandText = @" SELECT p.Id, p.Name, p.Price, SUM(o.Quantity) AS TotalSold FROM Products p JOIN Orders o ON o.ProductId = p.Id GROUP BY p.Id, p.Name, p.Price ORDER BY TotalSold DESC LIMIT @limit"; cmd.Parameters.Add(new CSharpDbParameter("@limit", limit)); var results = new List<Product>(); await using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { results.Add(new Product( reader.GetInt32(0), reader.GetString(1), reader.GetDouble(2), reader.GetInt32(3) )); } return results; } // Collection API for flexible document storage public async Task StoreSessionAsync(UserSession session) { var sessions = await _db.GetCollectionAsync<UserSession>("sessions"); await sessions.PutAsync(session.Id, session); } public async Task<UserSession?> GetSessionAsync(string id) { var sessions = await _db.GetCollectionAsync<UserSession>("sessions"); return await sessions.GetAsync(id); } public async ValueTask DisposeAsync() { _writes.Writer.Complete(); await _db.DisposeAsync(); } } public record Product(int Id, string Name, double Price, int TotalSold); public record UserSession(string Id, string UserId, DateTime Expires); // Query with the Engine API await using var result = await db.ExecuteAsync( "SELECT Name, Price FROM Products ORDER BY Price DESC LIMIT 5"); while (await result.MoveNextAsync()) Console.WriteLine($"{result.Current[0].AsText}: ${result.Current[1].AsReal}"); // ─── End of sample ─── // CSharpDB — Embedded Database Engine for .NET // Zero dependencies. ACID-compliant. Production-grade. using CSharpDB.Engine; using CSharpDB.Data; using System.Threading.Channels; namespace MyApp.DataLayer; public sealed class AppDatabase : IAsyncDisposable { private readonly Database _db; private readonly Channel<WriteOp> _writes; public static async Task<AppDatabase> OpenAsync(string path) { var db = await Database.OpenAsync(path); return new AppDatabase(db); } private AppDatabase(Database db) { _db = db; _writes = Channel.CreateBounded<WriteOp>(1024); } public async Task CreateSchemaAsync() { 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 Products ( Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Price REAL NOT NULL, Category TEXT, Created TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_products_category ON Products(Category); CREATE TABLE IF NOT EXISTS Orders ( Id INTEGER PRIMARY KEY, ProductId INTEGER REFERENCES Products(Id), Quantity INTEGER NOT NULL, Total REAL NOT NULL, OrderDate TEXT DEFAULT CURRENT_TIMESTAMP );"; await cmd.ExecuteNonQueryAsync(); } public async Task<IReadOnlyList<Product>> GetTopProductsAsync(int limit) { await using var conn = new CSharpDbConnection("Data Source=myapp.db"); await conn.OpenAsync(); await using var cmd = conn.CreateCommand(); cmd.CommandText = @" SELECT p.Id, p.Name, p.Price, SUM(o.Quantity) AS TotalSold FROM Products p JOIN Orders o ON o.ProductId = p.Id GROUP BY p.Id, p.Name, p.Price ORDER BY TotalSold DESC LIMIT @limit"; cmd.Parameters.Add(new CSharpDbParameter("@limit", limit)); var results = new List<Product>(); await using var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { results.Add(new Product( reader.GetInt32(0), reader.GetString(1), reader.GetDouble(2), reader.GetInt32(3) )); } return results; } }
The Embedded Database
Built for .NET
Zero dependencies. Full 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.
Full SQL Engine
DDL, DML, JOINs, aggregates, CTEs, subqueries, views, triggers, and stored procedures.
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.
import ctypes
db = ctypes.CDLL("./libcsharpdb.so")
db.csharpdb_open(b"app.db")
db.csharpdb_exec(b"INSERT INTO Users VALUES (1, 'Alice')")
result = db.csharpdb_query(b"SELECT * FROM Users")
db.csharpdb_close()
extern "C" {
fn csharpdb_open(path: *const c_char);
fn csharpdb_exec(sql: *const c_char);
fn csharpdb_query(sql: *const c_char) -> *const c_char;
fn csharpdb_close();
}
unsafe {
csharpdb_open(c"app.db".as_ptr());
csharpdb_exec(c"SELECT * FROM Users".as_ptr());
csharpdb_close();
}
// #cgo LDFLAGS: -L. -lcsharpdb
// #include "csharpdb.h"
import "C"
func main() {
C.csharpdb_open(C.CString("app.db"))
C.csharpdb_exec(C.CString("SELECT * FROM Users"))
C.csharpdb_close()
}
const ffi = require('ffi-napi');
const db = ffi.Library('./libcsharpdb', {
'csharpdb_open': ['void', ['string']],
'csharpdb_exec': ['void', ['string']],
'csharpdb_query': ['string', ['string']],
'csharpdb_close': ['void', []],
});
db.csharpdb_open("app.db");
db.csharpdb_exec("SELECT * FROM Users");
db.csharpdb_close();
Built with dotnet publish -r linux-x64 /p:NativeLib=Shared — produces a standard .so / .dll / .dylib with no runtime required.
Built for .NET
Not a port. Not a wrapper. Native .NET from the ground up.
NativeAOT Ready
Ahead-of-time compile your app and CSharpDB together. Sub-millisecond cold starts with no JIT overhead.
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.
No Reflection
Serialization uses source generators and compile-time analysis. Trimmer-safe, AOT-friendly, fully transparent.
How CSharpDB Compares
A fair look at where CSharpDB fits alongside other embedded options.
| Feature | CSharpDB | SQLite | LiteDB | RocksDB |
|---|---|---|---|---|
| Pure .NET / No native binaries | ✓ | — | ✓ | — |
| Full SQL (JOINs, CTEs, subqueries) | ✓ | ✓ | — | — |
| NoSQL Collection API | ✓ | — | ✓ | — |
| ACID transactions | ✓ | ✓ | ✓ | ✓ |
| Built-in ETL pipelines | ✓ | — | — | — |
| Full-text search | ✓ | ✓ | ✓ | — |
| REST API / gRPC | ✓ | — | — | — |
| Admin UI | ✓ | — | — | — |
| MCP server (AI agents) | ✓ | — | — | — |
| Mature ecosystem / battle-tested | — | ✓ | ✓ | ✓ |
| Collation & Unicode sorting | ✓ | ✓ | — | — |
| Multi-language SDKs (Python, Java, …) | ✓ | ✓ | — | ✓ |