Getting Started with CSharpDB

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: