This guide covers everything you need to install and configure WitDatabase in your .NET projects.


Prerequisites

System Requirements

Supported .NET Versions:

Version Support Notes
.NET 10.0 ✅ Full Recommended
.NET 9.0 ✅ Full Fully supported
.NET 8.0 ⚠️ Not tested May work, not officially supported
.NET Framework ❌ Not supported Use .NET 9+

Supported Platforms:

Platform Support Notes
Windows (x64, x86, ARM64) ✅ Full All features
Linux (x64, ARM64) ✅ Full All features
macOS (x64, Apple Silicon) ✅ Full All features
Blazor WebAssembly ✅ Full Requires IndexedDb package
iOS / Android (MAUI) ✅ Full File storage

Development Tools:

  • Visual Studio 2022 (17.8+) — recommended
  • Visual Studio Code with C# Dev Kit
  • JetBrains Rider 2024.1+
  • .NET CLI

Choosing the Right Package

WitDatabase is distributed as several NuGet packages. Choose based on your needs:

Package Use Case Includes
OutWit.Database.EntityFramework EF Core with LINQ, migrations, DbContext AdoNet, Core
OutWit.Database.AdoNet SQL queries with ADO.NET patterns Core
OutWit.Database.Core Direct key-value API, maximum control
OutWit.Database.Core.IndexedDb Blazor WebAssembly browser storage Core
OutWit.Database.Core.BouncyCastle ChaCha20-Poly1305 encryption Core

Decision Guide:

[[Svg Src="./witdatabase-package-decision-tree.svg" Alt=".NET Embedded Databases"]]


Installation Methods

.NET CLI

The simplest way to add WitDatabase to your project:

# For Entity Framework Core (most common)
dotnet add package OutWit.Database.EntityFramework

# For ADO.NET only
dotnet add package OutWit.Database.AdoNet

# For Core API only
dotnet add package OutWit.Database.Core

Additional packages:

# Blazor WebAssembly support
dotnet add package OutWit.Database.Core.IndexedDb

# ChaCha20 encryption (for WASM or ARM without AES-NI)
dotnet add package OutWit.Database.Core.BouncyCastle

NuGet Package Manager (Visual Studio)

  1. Right-click your project in Solution Explorer
  2. Select Manage NuGet Packages...
  3. Search for OutWit.Database.EntityFramework
  4. Click Install

Or use the Package Manager Console:

Install-Package OutWit.Database.EntityFramework

PackageReference (Manual)

Add directly to your .csproj file:

<ItemGroup>
  <PackageReference Include="OutWit.Database.EntityFramework" Version="1.0.0" />
</ItemGroup>

For multiple packages:

<ItemGroup>
  <!-- EF Core support -->
  <PackageReference Include="OutWit.Database.EntityFramework" Version="1.0.0" />
  
  <!-- Blazor WASM support -->
  <PackageReference Include="OutWit.Database.Core.IndexedDb" Version="1.0.0" />
  
  <!-- ChaCha20 encryption -->
  <PackageReference Include="OutWit.Database.Core.BouncyCastle" Version="1.0.0" />
</ItemGroup>

Package Dependencies

Packages automatically include their dependencies:

OutWit.Database.EntityFramework
└── OutWit.Database.AdoNet
    └── OutWit.Database.Core

OutWit.Database.Core.IndexedDb
└── OutWit.Database.Core

OutWit.Database.Core.BouncyCastle
└── OutWit.Database.Core

Tip: Install only the highest-level package you need. Dependencies are included automatically.


Platform-Specific Setup

Console Application

1. Create a new project:

dotnet new console -n MyApp
cd MyApp
dotnet add package OutWit.Database.AdoNet

2. Minimal example (Program.cs):

using OutWit.Database.AdoNet;

// Create and open connection
using var connection = new WitDbConnection("Data Source=myapp.witdb");
connection.Open();

// Create a table
using var cmd = connection.CreateCommand();
cmd.CommandText = """
    CREATE TABLE IF NOT EXISTS Users (
        Id INTEGER PRIMARY KEY AUTOINCREMENT,
        Name VARCHAR(100) NOT NULL,
        Email VARCHAR(255) UNIQUE
    )
    """;
cmd.ExecuteNonQuery();

// Insert data
cmd.CommandText = "INSERT INTO Users (Name, Email) VALUES (@name, @email)";
cmd.Parameters.Add(new WitDbParameter("@name", "John Doe"));
cmd.Parameters.Add(new WitDbParameter("@email", "john@example.com"));
cmd.ExecuteNonQuery();

Console.WriteLine("Database created and data inserted!");

// Query data
cmd.Parameters.Clear();
cmd.CommandText = "SELECT * FROM Users";
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
    Console.WriteLine(
Loading...
quot;User: {reader["Id"]} - {reader["Name"]} ({reader["Email"]})"); }

3. Run:

dotnet run

ASP.NET Core Web API

1. Create a new project:

dotnet new webapi -n MyWebApi
cd MyWebApi
dotnet add package OutWit.Database.EntityFramework

2. Create a DbContext (Data/AppDbContext.cs):

using Microsoft.EntityFrameworkCore;

namespace MyWebApi.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) 
        : base(options) { }

    public DbSet<User> Users => Set<User>();
    public DbSet<Product> Products => Set<Product>();
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}

3. Configure in Program.cs:

using Microsoft.EntityFrameworkCore;
using MyWebApi.Data;
using OutWit.Database.EntityFramework;

var builder = WebApplication.CreateBuilder(args);

// Add WitDatabase with EF Core
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseWitDb("Data Source=mywebapi.witdb"));

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Ensure database is created
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    db.Database.EnsureCreated();
}

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

4. Create a controller (Controllers/UsersController.cs):

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyWebApi.Data;

namespace MyWebApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly AppDbContext _context;

    public UsersController(AppDbContext context)
    {
        _context = context;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<User>>> GetUsers()
    {
        return await _context.Users.ToListAsync();
    }

    [HttpPost]
    public async Task<ActionResult<User>> CreateUser(User user)
    {
        _context.Users.Add(user);
        await _context.SaveChangesAsync();
        return CreatedAtAction(nameof(GetUsers), new { id = user.Id }, user);
    }
}

WPF / WinForms / MAUI Desktop

1. Add package:

dotnet add package OutWit.Database.EntityFramework

2. Choose database location:

// Option 1: Application data folder (recommended for user data)
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var dbPath = Path.Combine(appDataPath, "MyApp", "data.witdb");
Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);

// Option 2: Application directory (for portable apps)
var dbPath = Path.Combine(AppContext.BaseDirectory, "data.witdb");

// Option 3: User's documents
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var dbPath = Path.Combine(documentsPath, "MyApp", "data.witdb");

3. Configure DbContext:

public class AppDbContext : DbContext
{
    private readonly string _dbPath;

    public AppDbContext(string dbPath)
    {
        _dbPath = dbPath;
    }

    public DbSet<Document> Documents => Set<Document>();

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseWitDb(
Loading...
quot;Data Source={_dbPath}"); } }

4. With encryption (for sensitive user data):

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
    options.UseWitDb(
Loading...
quot;Data Source={_dbPath};Encryption=aes-gcm;Password={_userPassword}"); }

Blazor WebAssembly

1. Add packages:

dotnet add package OutWit.Database.Core.IndexedDb
dotnet add package OutWit.Database.Core.BouncyCastle  # Optional: for encryption

2. Add JavaScript files to wwwroot/index.html:

<!DOCTYPE html>
<html>
<head>
    <!-- ... existing head content ... -->
</head>
<body>
    <div id="app">Loading...</div>
    
    <!-- Add before the blazor script -->
    <script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb.js"></script>
    <script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb-index.js"></script>
    
    <script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

3. Create a database service:

using Microsoft.JSInterop;
using OutWit.Database.Core.Builder;
using OutWit.Database.Core.IndexedDb;

public class DatabaseService : IAsyncDisposable
{
    private readonly IJSRuntime _jsRuntime;
    private WitDatabase? _database;

    public DatabaseService(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public async Task InitializeAsync()
    {
        _database = new WitDatabaseBuilder()
            .WithIndexedDbStorage("MyBlazorApp", _jsRuntime)
            .WithBTree()
            .WithTransactions()
            .Build();

        // Initialize IndexedDB storage
        if (_database.Store is StorageIndexedDb indexedDbStorage)
        {
            await indexedDbStorage.InitializeAsync();
        }
    }

    public WitDatabase Database => _database 
        ?? throw new InvalidOperationException("Database not initialized. Call InitializeAsync first.");

    public async ValueTask DisposeAsync()
    {
        if (_database != null)
        {
            await _database.DisposeAsync();
        }
    }
}

4. Register in Program.cs:

builder.Services.AddScoped<DatabaseService>();

5. Use in a component:

@page "/data"
@inject DatabaseService DbService
@implements IAsyncDisposable

<h3>Data Manager</h3>

@if (_initialized)
{
    <button @onclick="SaveData">Save</button>
    <button @onclick="LoadData">Load</button>
    <p>@_message</p>
}
else
{
    <p>Initializing database...</p>
}

@code {
    private bool _initialized;
    private string _message = "";

    protected override async Task OnInitializedAsync()
    {
        await DbService.InitializeAsync();
        _initialized = true;
    }

    private async Task SaveData()
    {
        var key = System.Text.Encoding.UTF8.GetBytes("test-key");
        var value = System.Text.Encoding.UTF8.GetBytes(
Loading...
quot;Saved at {DateTime.Now}"); await DbService.Database.PutAsync(key, value); _message = "Data saved!"; } private async Task LoadData() { var key = System.Text.Encoding.UTF8.GetBytes("test-key"); var value = await DbService.Database.GetAsync(key); _message = value != null ? System.Text.Encoding.UTF8.GetString(value) : "No data found"; } public async ValueTask DisposeAsync() { await DbService.DisposeAsync(); } }

With encryption in browser:

// Use ChaCha20 for better WASM performance (no AES-NI in browser)
_database = new WitDatabaseBuilder()
    .WithIndexedDbStorage("MyBlazorApp", _jsRuntime)
    .WithBTree()
    .WithBouncyCastleEncryption("user-password")  // ChaCha20-Poly1305
    .WithTransactions()
    .Build();

Configuration

Connection String Format

WitDatabase uses a connection string format similar to other ADO.NET providers:

Data Source=path/to/database.witdb;Encryption=aes-gcm;Password=secret;Store=btree

Full Parameter Reference:

Parameter Type Default Description
Core
Data Source string Path to database file, or :memory: for in-memory
Mode enum ReadWriteCreate ReadOnly, ReadWrite, ReadWriteCreate
Storage Engine
Store enum BTree BTree, LSM
Encryption
Encryption enum None None, AesGcm, ChaCha20
Password string Encryption password
User string Username for salt derivation
Fast Encryption bool false Use faster PBKDF2 iterations (for WASM)
Transactions
Transactions bool true Enable ACID transactions
MVCC bool true Enable Multi-Version Concurrency Control
Isolation Level enum ReadCommitted Default isolation level
Concurrency
Parallel Mode enum None None, Auto, Latched, Buffered
Max Writers int CPU count Max parallel writers (for Buffered mode)
Performance
Cache Size int 1000 Number of pages in cache
Page Size int 4096 Page size in bytes
Connection Pool
Pooling bool true Enable connection pooling
Min Pool Size int 0 Minimum connections in pool
Max Pool Size int 100 Maximum connections in pool

Example connection strings:

// Simple file database
"Data Source=app.witdb"

// In-memory database
"Data Source=:memory:"

// With encryption
"Data Source=secure.witdb;Encryption=aes-gcm;Password=MySecretPassword123!"

// With all options
"Data Source=app.witdb;Store=btree;Encryption=aes-gcm;Password=secret;MVCC=true;Isolation Level=Snapshot;Cache Size=2000"

// For multi-threaded access
"Data Source=app.witdb;Parallel Mode=Auto"

// Read-only mode
"Data Source=app.witdb;Mode=ReadOnly"

Using WitDbConnectionStringBuilder

For programmatic connection string construction:

using OutWit.Database.AdoNet;

var builder = new WitDbConnectionStringBuilder
{
    DataSource = "myapp.witdb",
    Encryption = WitDbEncryptionType.AesGcm,
    Password = "MySecretPassword",
    Mvcc = true,
    IsolationLevel = WitDbIsolationLevel.Snapshot,
    CacheSize = 2000
};

using var connection = new WitDbConnection(builder.ConnectionString);

Builder Pattern (Core API)

For direct Core API usage, the builder pattern provides full control:

using OutWit.Database.Core.Builder;

var db = new WitDatabaseBuilder()
    // Storage location
    .WithFilePath("data.witdb")           // File storage
    // .WithMemoryStorage()                // In-memory (testing)
    
    // Storage engine
    .WithBTree()                           // Read-optimized (default)
    // .WithLsmTree()                      // Write-optimized
    
    // Encryption
    .WithEncryption("password")            // AES-256-GCM
    // .WithBouncyCastleEncryption("pw")   // ChaCha20-Poly1305
    
    // Transactions
    .WithTransactions()                    // Enable ACID
    .WithMvcc()                            // Enable MVCC
    .WithDefaultIsolationLevel(IsolationLevel.Snapshot)
    
    // Concurrency
    .WithFileLocking()                     // File-level locking
    .WithParallelMode(ParallelMode.Auto)   // Thread-safe mode
    
    // Performance
    .WithCacheSize(2000)                   // Pages in cache
    .WithPageSize(4096)                    // Bytes per page
    
    .Build();

Builder Methods Reference:

Category Method Description
Storage WithFilePath(path) Use file storage
WithMemoryStorage() Use in-memory storage
WithIndexedDbStorage(name, js) Use browser IndexedDB
Engine WithBTree() B+Tree engine (read-optimized)
WithLsmTree() LSM-Tree engine (write-optimized)
WithLsmTree(dir, opts) LSM-Tree with options
Encryption WithEncryption(password) AES-256-GCM
WithEncryption(user, password) AES-GCM with user salt
WithEncryptionFast(password) AES-GCM optimized for WASM
WithBouncyCastleEncryption(pw) ChaCha20-Poly1305
WithAesEncryption(key) AES-GCM with raw 256-bit key
Transactions WithTransactions() Enable transactions
WithoutTransactions() Disable (for performance)
WithMvcc() Enable MVCC
WithDefaultIsolationLevel(level) Set default isolation
Concurrency WithFileLocking() Enable file locking
WithoutFileLocking() Disable file locking
WithParallelMode(mode) Set parallel access mode
WithLockTimeout(timeout) Set lock timeout
Performance WithCacheSize(pages) Set cache size
WithPageSize(bytes) Set page size

Entity Framework Core Configuration

Basic configuration:

services.AddDbContext<AppDbContext>(options =>
    options.UseWitDb("Data Source=app.witdb"));

With connection string from configuration:

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=app.witdb;Encryption=aes-gcm;Password=secret"
  }
}

// Program.cs
services.AddDbContext<AppDbContext>(options =>
    options.UseWitDb(Configuration.GetConnectionString("DefaultConnection")));

With additional options:

services.AddDbContext<AppDbContext>(options =>
    options.UseWitDb("Data Source=app.witdb", witOptions =>
    {
        witOptions.UseParallelWrites(WitDbParallelMode.Auto);
        witOptions.MaxWriters(8);
    }));

Factory pattern for multiple databases:

services.AddDbContextFactory<AppDbContext>(options =>
    options.UseWitDb("Data Source=app.witdb"));

// Usage
public class MyService
{
    private readonly IDbContextFactory<AppDbContext> _factory;
    
    public MyService(IDbContextFactory<AppDbContext> factory)
    {
        _factory = factory;
    }
    
    public async Task DoWorkAsync()
    {
        await using var context = await _factory.CreateDbContextAsync();
        // Use context...
    }
}

Verifying Installation

Quick Test (ADO.NET)

Create a simple test to verify everything works:

using OutWit.Database.AdoNet;

try
{
    // Test in-memory database
    using var connection = new WitDbConnection("Data Source=:memory:");
    connection.Open();
    
    Console.WriteLine(
Loading...
quot;✓ Connection opened successfully"); Console.WriteLine(
Loading...
quot; Server Version: {connection.ServerVersion}"); Console.WriteLine(
Loading...
quot; State: {connection.State}"); // Test query execution using var cmd = connection.CreateCommand(); cmd.CommandText = "SELECT 1 + 1 AS Result"; var result = cmd.ExecuteScalar(); Console.WriteLine(
Loading...
quot;✓ Query executed: 1 + 1 = {result}"); Console.WriteLine(); Console.WriteLine("WitDatabase is installed and working correctly!"); } catch (Exception ex) { Console.WriteLine(
Loading...
quot;✗ Error: {ex.Message}"); }

Quick Test (Entity Framework Core)

using Microsoft.EntityFrameworkCore;
using OutWit.Database.EntityFramework;

// Simple test context
public class TestDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseWitDb("Data Source=:memory:");
    
    public DbSet<TestEntity> TestEntities => Set<TestEntity>();
}

public class TestEntity
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

// Test
try
{
    using var context = new TestDbContext();
    context.Database.EnsureCreated();
    
    Console.WriteLine("✓ Database created successfully");
    
    // Test insert
    context.TestEntities.Add(new TestEntity { Name = "Test" });
    context.SaveChanges();
    
    Console.WriteLine("✓ Entity inserted successfully");
    
    // Test query
    var entity = context.TestEntities.First();
    Console.WriteLine(
Loading...
quot;✓ Entity retrieved: Id={entity.Id}, Name={entity.Name}"); Console.WriteLine(); Console.WriteLine("Entity Framework Core provider is working correctly!"); } catch (Exception ex) { Console.WriteLine(
Loading...
quot;✗ Error: {ex.Message}"); }

Common Installation Issues

1. Package not found

error NU1101: Unable to find package OutWit.Database.EntityFramework

Solution: Ensure you have the correct NuGet source configured:

dotnet nuget list source
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org

2. Version conflicts

error NU1107: Version conflict detected for OutWit.Database.Core

Solution: Ensure all WitDatabase packages are the same version:

<ItemGroup>
  <PackageReference Include="OutWit.Database.EntityFramework" Version="1.0.0" />
  <PackageReference Include="OutWit.Database.Core.IndexedDb" Version="1.0.0" />
</ItemGroup>

3. Blazor WASM: JavaScript files not found

Error: Could not find 'witdb-indexeddb.js'

Solution: Verify the script tags in index.html:

<script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb.js"></script>
<script src="_content/OutWit.Database.Core.IndexedDb/witdb-indexeddb-index.js"></script>

4. File access denied

System.UnauthorizedAccessException: Access to the path 'data.witdb' is denied.

Solution: Check file permissions or use a different location:

// Use AppData folder instead
var path = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
    "MyApp",
    "data.witdb");
Directory.CreateDirectory(Path.GetDirectoryName(path)!);

5. Database locked

WitDbException: Database file is locked by another process

Solution: Ensure only one process accesses the database, or use parallel mode:

// For multi-process scenarios
"Data Source=app.witdb;Parallel Mode=Auto"

Upgrading

From Previous Versions

When upgrading WitDatabase packages:

1. Update all packages together:

dotnet add package OutWit.Database.EntityFramework --version 1.1.0
dotnet add package OutWit.Database.Core.IndexedDb --version 1.1.0

2. Check for breaking changes in the release notes.

3. Test your application thoroughly before deploying.

Database File Compatibility

WitDatabase maintains backward compatibility for database files:

  • Files created with v1.0 can be opened with v1.x
  • Major version upgrades may require migration (documented in release notes)

Uninstalling

Removing Packages

dotnet remove package OutWit.Database.EntityFramework
dotnet remove package OutWit.Database.AdoNet
dotnet remove package OutWit.Database.Core
dotnet remove package OutWit.Database.Core.IndexedDb
dotnet remove package OutWit.Database.Core.BouncyCastle

Cleaning Up

1. Remove database files:

// Find and delete database files
File.Delete("app.witdb");
File.Delete("app.witdb-journal");  // If using rollback journal
Directory.Delete("app.witdb-wal", true);  // If using WAL

2. Remove Blazor WASM scripts from index.html if applicable.

3. Remove any configuration from appsettings.json.