CSharpDB is a zero-dependency embedded database engine for .NET. It gives you full SQL support, a typed Collection API, and a low-level storage engine — all in a single NuGet package with no native binaries to manage.
In this tutorial, you'll go from installation to running queries in under five minutes.
Prerequisites
You need the .NET 10 SDK (or later) installed. Any project type works — console app, ASP.NET, MAUI, Blazor, or a class library.
Step 1: Install the Package
Add CSharpDB to your project via the .NET CLI:
dotnet add package CSharpDB
This meta-package pulls in the storage engine, SQL engine, Collection API, ADO.NET provider, and diagnostics. If you only need a subset, you can install individual packages like CSharpDB.Storage, CSharpDB.Engine, or CSharpDB.Data.
Step 2: Create a Database and Run SQL
The simplest way to use CSharpDB is through the Database class in CSharpDB.Engine. It opens (or creates) a single-file database and lets you execute SQL directly:
using CSharpDB.Engine;
// Open or create a database file
await using var db = await Database.OpenAsync("myapp.db");
// Create a table
await db.ExecuteAsync(@"
CREATE TABLE IF NOT EXISTS Products (
Id INTEGER PRIMARY KEY,
Name TEXT NOT NULL,
Price REAL
)");
// Insert data
await db.ExecuteAsync("INSERT INTO Products VALUES (1, 'Widget', 9.99)");
await db.ExecuteAsync("INSERT INTO Products VALUES (2, 'Gadget', 24.99)");
await db.ExecuteAsync("INSERT INTO Products VALUES (3, 'Doohickey', 4.50)");
// Query and read results
await using var results = await db.ExecuteAsync(
"SELECT Name, Price FROM Products WHERE Price > 5 ORDER BY Price DESC");
while (await results.MoveNextAsync())
{
var name = results.Current[0].AsText;
var price = results.Current[1].AsReal;
Console.WriteLine($"{name}: ${price:F2}");
}
Output:
Gadget: $24.99
Widget: $9.99
That's it — no connection strings, no server process, no configuration files. The database is a single .db file on disk with write-ahead logging and crash recovery built in.
Step 3: Use ADO.NET for Familiar Patterns
If you prefer the standard .NET data access pattern with DbConnection and DbCommand, CSharpDB includes a full ADO.NET provider:
using CSharpDB.Data;
await using var conn = new CSharpDbConnection("Data Source=myapp.db");
await conn.OpenAsync();
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT Name, Price FROM Products WHERE Price > @min";
cmd.Parameters.Add(new CSharpDbParameter("@min", 5.0));
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
Console.WriteLine($"{reader.GetString(0)}: ${reader.GetDouble(1):F2}");
}
The ADO.NET provider supports parameterized queries, connection pooling, transactions, and GetSchema() for ORM/tooling compatibility.
Step 4: Store Objects with the Collection API
Don't need SQL? The Collection API lets you store and retrieve typed C# objects with zero mapping code:
using CSharpDB.Engine;
public record Product(string Name, double Price, string[] Tags);
await using var db = await Database.OpenAsync("myapp.db");
var products = await db.GetCollectionAsync<Product>("products");
// Store objects by key
await products.PutAsync("widget", new("Widget", 9.99, ["tools", "hardware"]));
await products.PutAsync("gadget", new("Gadget", 24.99, ["electronics"]));
// Retrieve by key
var widget = await products.GetAsync("widget");
Console.WriteLine($"{widget!.Name}: ${widget.Price:F2}");
// Create an index on a property
await products.EnsureIndexAsync(p => p.Name);
// Query by indexed property
await foreach (var match in products.FindByIndexAsync(p => p.Name, "Gadget"))
Console.WriteLine($"Found: {match.Key}");
// Scan all entries
await foreach (var entry in products.ScanAsync())
Console.WriteLine($"{entry.Key}: {entry.Value.Name}");
Collections support nested path indexes, array-element indexes, and multiple index types — all backed by the same B+tree storage engine.
Step 5: Choose Your Database Mode
CSharpDB supports three modes depending on your performance and durability needs:
using CSharpDB.Engine;
// 1. Disk-based (default) — full durability with WAL
await using var disk = await Database.OpenAsync("myapp.db");
// 2. In-memory — fastest, no persistence
await using var mem = await Database.OpenInMemoryAsync();
// 3. Hybrid — hot tables in memory, durable checkpoint to disk
await using var hybrid = await Database.OpenHybridAsync(
"myapp.db",
new DatabaseOptions(),
new HybridDatabaseOptions
{
PersistenceMode = HybridPersistenceMode.IncrementalDurable,
HotTableNames = ["Sessions", "Cache"]
});
Step 6: Full-Text Search
CSharpDB includes built-in full-text search with tokenization, stemming, and relevance ranking:
// Create a full-text index on the Name column
await db.ExecuteAsync("CREATE FULLTEXT INDEX idx_ft_name ON Products(Name)");
// Search with ranking
await using var results = await db.ExecuteAsync(
"SELECT Name, Price FROM Products WHERE MATCH(Name, 'widget gadget')");
while (await results.MoveNextAsync())
Console.WriteLine(results.Current[0].AsText);
What's Next?
You now have a fully working CSharpDB setup. Here are some paths to explore:
- SQL Reference — JOINs, aggregates, CTEs, subqueries, and all supported syntax
- Collections API — Deep dive into indexes, nested paths, and scanning
- ETL Pipelines — Build data transformation workflows
- Tools & Ecosystem — CLI, Admin UI, REST API, gRPC, and MCP server
- Performance Guide — Optimize CSharpDB for your workload