GaldrDbEngine 1.0.0-rc2

This is a prerelease version of GaldrDbEngine.
dotnet add package GaldrDbEngine --version 1.0.0-rc2
                    
NuGet\Install-Package GaldrDbEngine -Version 1.0.0-rc2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GaldrDbEngine" Version="1.0.0-rc2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GaldrDbEngine" Version="1.0.0-rc2" />
                    
Directory.Packages.props
<PackageReference Include="GaldrDbEngine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GaldrDbEngine --version 1.0.0-rc2
                    
#r "nuget: GaldrDbEngine, 1.0.0-rc2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GaldrDbEngine@1.0.0-rc2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GaldrDbEngine&version=1.0.0-rc2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=GaldrDbEngine&version=1.0.0-rc2&prerelease
                    
Install as a Cake Tool

GaldrDb

Native AOT compatible single-file document database for .NET 8.0, 9.0, and 10.0.

GaldrDb is a strongly typed, single-file document database for .NET that works everywhere your app does — servers, desktop on Windows, macOS, and Linux (x64 and ARM64), and mobile. It works great with standard .NET, but its source generators validate queries at compile time with zero reflection, giving you full Native AOT compatibility where ORMs like EF Core still struggle. Just one NuGet package, no external dependencies.

Features

  • Type-safe queries validated at compile time
  • ACID transactions with snapshot isolation and optimistic concurrency
    • Automatic retry with jitter on page-level conflicts
  • Fluent query API with filtering, sorting, pagination, and projections
    • Equality, range, set membership, and string operations (StartsWith, Contains, EndsWith)
  • Indexes with automatic query plan optimization
    • Single-field and compound (multi-field) indexes
    • Unique constraints
    • Nested document path indexes
  • Write-ahead logging (WAL) for crash recovery and durability
  • Encryption at rest with AES-256-GCM and per-page nonces (optional)
  • Dynamic API for runtime schema flexibility with JSON documents
  • Native AOT compatible — no reflection, no runtime code generation

Quick Start

Installation

dotnet add package GaldrDbEngine --prerelease

GaldrDb is currently in release candidate. Once stable, use dotnet add package GaldrDbEngine.

Example

// Define a model
[GaldrDbCollection]
public class Book
{
    public int Id { get; set; }
    [GaldrDbIndex]
    public string Title { get; set; }
    public string Author { get; set; }
}

// Create a database
GaldrDbOptions options = new() { PageSize = 8192, UseWal = true };
GaldrDb db = GaldrDb.Create("books.db", options);

// Insert a document
int id = db.Insert(new Book { Title = "The Pragmatic Programmer", Author = "Andy Hunt" });

// Query documents
Book book = db.Query<Book>()
    .Where(BookMeta.Title, FieldOp.Equals, "The Pragmatic Programmer")
    .FirstOrDefault();

Getting Started

Creating and Opening Databases

// Create a new database
GaldrDb db = GaldrDb.Create("database.db", new GaldrDbOptions());

// Open an existing database
GaldrDb db = GaldrDb.Open("database.db");

// Open or create as needed
GaldrDb db = GaldrDb.OpenOrCreate("database.db");

Defining Models

[GaldrDbCollection]
public class Person
{
    public int Id { get; set; }
    [GaldrDbIndex]
    public string Name { get; set; }
    public int Age { get; set; }
}

[GaldrDbCollection("customers")]
public class Customer
{
    public int Id { get; set; }
    [GaldrDbIndex(Unique = true)]
    public string Email { get; set; }
}

Basic CRUD Operations

// Insert
int id = db.Insert(new Person { Name = "Alice", Age = 30 });

// Get by ID
Person person = db.GetById<Person>(id);

// Replace
person.Age = 31;
db.Replace(person);

// Delete
db.DeleteById<Person>(id);

// Partial update (update specific fields without loading the full document)
db.UpdateById<Person>(id)
    .Set(PersonMeta.Age, 32)
    .Execute();

Querying

// Filter
List<Person> adults = db.Query<Person>()
    .Where(PersonMeta.Age, FieldOp.GreaterThanOrEqual, 18)
    .ToList();

// Range query
List<Person> people = db.Query<Person>()
    .WhereBetween(PersonMeta.Age, 25, 40)
    .ToList();

// Sort and limit
List<Person> results = db.Query<Person>()
    .OrderByDescending(PersonMeta.Age)
    .Limit(10)
    .ToList();

// In/NotIn queries
List<Person> selected = db.Query<Person>()
    .WhereIn(PersonMeta.Name, "Alice", "Bob", "Charlie")
    .ToList();

// Pagination
List<Person> page = db.Query<Person>()
    .OrderBy(PersonMeta.Name)
    .Skip(20)
    .Limit(10)
    .ToList();

// Check existence
bool hasAdults = db.Query<Person>()
    .Where(PersonMeta.Age, FieldOp.GreaterThanOrEqual, 18)
    .Any();

// Query explanation (shows execution plan)
QueryExplanation explanation = db.Query<Person>()
    .Where(PersonMeta.Age, FieldOp.GreaterThan, 21)
    .Explain();

ASP.NET Core Integration

Install the GaldrDbAspNetCore package for dependency injection support:

dotnet add package GaldrDbAspNetCore --prerelease

Basic setup (single database):

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddGaldrDb(options =>
{
    options.FilePath = "app.db";
    options.OpenMode = GaldrDbOpenMode.OpenOrCreate;
});

WebApplication app = builder.Build();

// Inject IGaldrDb directly
app.MapGet("/users/{id}", (IGaldrDb db, int id) =>
{
    return db.GetById<User>(id);
});

Multiple databases (named instances):

// Register named databases
builder.Services.AddGaldrDb(options =>
{
    options.FilePath = "users.db";
});

builder.Services.AddGaldrDb("orders", options =>
{
    options.FilePath = "orders.db";
});

// Access named instances via IGaldrDbFactory
app.MapGet("/orders/{id}", (IGaldrDbFactory factory, int id) =>
{
    IGaldrDb db = factory.Get("orders");
    return db.GetById<Order>(id);
});

Advanced Features

Transactions

using (ITransaction tx = db.BeginTransaction())
{
    Person person = tx.GetById<Person>(id);
    person.Age++;
    tx.Replace(person);
    tx.Commit();
}

Indexes

Add [GaldrDbIndex] to properties to create indexes. Use Unique = true to enforce uniqueness:

[GaldrDbCollection]
public class Product
{
    public int Id { get; set; }
    [GaldrDbIndex]
    public string Category { get; set; }
    [GaldrDbIndex(Unique = true)]
    public string Sku { get; set; }
}

List<Product> products = db.Query<Product>()
    .Where(ProductMeta.Category, FieldOp.Equals, "Electronics")
    .ToList();
Compound Indexes

Compound indexes optimize queries that filter on multiple fields. Define them at the class level:

[GaldrDbCollection]
[GaldrDbCompoundIndex("Status", "CreatedDate")]
[GaldrDbCompoundIndex("Category", "Priority")]
public class Order
{
    public int Id { get; set; }
    public string Status { get; set; }
    public DateTime CreatedDate { get; set; }
    public string Category { get; set; }
    public int Priority { get; set; }
}

// Uses the Status_CreatedDate compound index
List<Order> orders = db.Query<Order>()
    .Where(OrderMeta.Status, FieldOp.Equals, "Pending")
    .Where(OrderMeta.CreatedDate, FieldOp.GreaterThan, DateTime.Today.AddDays(-7))
    .ToList();

// Prefix queries also use the compound index
List<Order> pendingOrders = db.Query<Order>()
    .Where(OrderMeta.Status, FieldOp.Equals, "Pending")
    .ToList();

Compound indexes follow the leftmost-prefix rule: an index on (A, B, C) can serve queries on A, A AND B, or A AND B AND C. The query planner automatically selects the best index based on filter selectivity.

Nested Document Indexes

Indexes work on nested object properties too. Add [GaldrDbIndex] to nested class properties, or use dot notation in compound indexes:

public class Address
{
    [GaldrDbIndex]
    public string City { get; set; }
    public string Country { get; set; }
}

[GaldrDbCollection]
[GaldrDbCompoundIndex("Status", "Address.City")]
public class Person
{
    public int Id { get; set; }
    public string Status { get; set; }
    public Address Address { get; set; }
}

// Uses index on Address.City
List<Person> results = db.Query<Person>()
    .Where(PersonMeta.Address.City, FieldOp.Equals, "Seattle")
    .ToList();

// Uses compound index
List<Person> active = db.Query<Person>()
    .Where(PersonMeta.Status, FieldOp.Equals, "Active")
    .Where(PersonMeta.Address.City, FieldOp.Equals, "Seattle")
    .ToList();

Null handling follows standard database semantics: null nested values are excluded from equality queries, and multiple null values are allowed for unique indexes (NULL != NULL).

Collection Field Queries

Query documents based on elements within arrays or lists:

[GaldrDbCollection]
public class Order
{
    public int Id { get; set; }
    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public string ProductName { get; set; }
    public decimal Price { get; set; }
}

// Find orders containing items over $100
List<Order> results = db.Query<Order>()
    .WhereAny(OrderMeta.Items.Price, FieldOp.GreaterThan, 100m)
    .ToList();

Projections

Projections let you query a subset of fields from a document without deserializing the entire object. Define a projection class with only the fields you need:

[GaldrDbCollection]
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public Address Address { get; set; }
}

[GaldrDbProjection(typeof(Person))]
public partial class PersonSummary
{
    public int Id { get; set; }
    public string Name { get; set; }
}

List<PersonSummary> summaries = db.Query<PersonSummary>().ToList();

Vacuum and Compact

// Remove old versions and compact pages
GarbageCollectionResult result = db.Vacuum();

// Create a compacted copy
DatabaseCompactResult compactResult = db.CompactTo("compacted.db");

Encryption at Rest

// Create an encrypted database
GaldrDbOptions options = new()
{
    Encryption = new EncryptionOptions
    {
        Password = "your-secret-password",
        KdfIterations = 500000,  // PBKDF2 iterations (higher = more secure, slower open)
    }
};
GaldrDb db = GaldrDb.Create("encrypted.db", options);

// Open an encrypted database (same password required)
GaldrDb db = GaldrDb.Open("encrypted.db", options);

Encryption uses AES-256-GCM with per-page nonces. The WAL file is also encrypted when encryption is enabled.

Checkpoint Control

// Manually checkpoint WAL to main database file
db.Checkpoint();

// Supports async
await db.CheckpointAsync();

Dynamic API

For scenarios where types aren't known at compile time, use the dynamic API with JSON strings:

// Insert with JSON
int id = db.InsertDynamic("people", """{"Name": "Alice", "Age": 30}""");

// Get by ID (returns JsonDocument)
JsonDocument doc = db.GetByIdDynamic("people", id);

// Replace
db.ReplaceDynamic("people", id, """{"Id": 1, "Name": "Alice", "Age": 31}""");

// Partial update
db.UpdateByIdDynamic("people", id)
    .Set("Age", 32)
    .Execute();

// Query
List<JsonDocument> results = db.QueryDynamic("people")
    .Where("Age", FieldOp.GreaterThan, 25)
    .OrderBy("Name")
    .ToList();

// Delete
db.DeleteByIdDynamic("people", id);

Architecture & Design Decisions

Storage Engine

  • Page-based storage with configurable page size (default 8KB)
  • Large document support — documents automatically span multiple pages when needed
  • B+ tree indexing for primary key lookups, range scans, and secondary indexes
  • Page cache with configurable size for frequently accessed pages
  • Encryption at rest — optional AES-256-GCM encryption with per-page nonces

Concurrency Model

  • MVCC with snapshot isolation — readers never block writers, each transaction sees a consistent point-in-time view
  • Optimistic concurrency control with conflict detection and automatic retry at commit time

Durability

  • Write-Ahead Logging (WAL) — all writes are logged before applying to the main database, with checksums for data integrity
  • Crash recovery — committed transactions are replayed automatically on startup
  • Automatic checkpointing — WAL changes are periodically merged to the main database

Configuration Options

GaldrDbOptions options = new()
{
    PageSize = 8192,                      // Page size (must be power of 2, >= 1024)
    UseWal = true,                        // Enable write-ahead logging
    AutoCheckpoint = true,                // Automatically checkpoint WAL
    WalCheckpointThreshold = 1000,        // Frames before auto-checkpoint
    UseMmap = false,                      // Use memory-mapped files (with auto-fallback)
    WarmupOnOpen = true,                  // Warmup pools on database open
    JsonWriterBufferSize = 4096,          // Initial buffer size for JSON serialization
    JsonWriterPoolWarmupCount = 4,        // JSON writer pool warmup size
    AutoGarbageCollection = true,         // Automatically collect old versions
    GarbageCollectionThreshold = 250,     // Commits before auto-gc
    ExpansionPageCount = 256,             // Pages to add on expansion (2MB with 8KB pages)
    PageCacheSize = 2000,                 // Max cached pages (16MB with 8KB pages)
    Encryption = new EncryptionOptions    // Optional encryption (null = disabled)
    {
        Password = "your-secret-password",
        KdfIterations = 500000,           // PBKDF2 iterations for key derivation
    },
};

Benchmarks

These are the current benchmark results from SingleOperationAotBenchmarks and SingleOperationBenchmarks. The results are of the current in development build, with future optimizations planned. Additional benchmarks with more advanced queries will be added at a later date.

Setup

BenchmarkDotNet v0.15.8, Linux Fedora Linux 43 (Workstation Edition) AMD Ryzen AI 7 PRO 350 w/ Radeon 860M 0.62GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK 10.0.101

InvocationCount=16384 IterationCount=20 WarmupCount=3

SingleOperationAotBenchmarks

Native AOT comparison between GaldrDb and SQLite ADO.NET. Both use WAL mode with equivalent durability guarantees (synchronous=FULL). GaldrDb shows strong read and delete performance; write performance is an area of active optimization.

Method Mean Error StdDev Rank Gen0 Gen1 Allocated
'GaldrDb Delete' 272.9 ns 11.28 ns 12.99 ns 1 0.0610 - 713 B
'GaldrDb Read' 1,795.1 ns 79.36 ns 84.92 ns 2 0.1221 - 1374 B
'SQLite ADO.NET Delete' 2,476.2 ns 19.04 ns 20.37 ns 3 0.0610 - 768 B
'SQLite ADO.NET Update' 3,146.7 ns 15.25 ns 16.95 ns 4 0.1221 - 1200 B
'SQLite ADO.NET Read' 4,695.1 ns 32.25 ns 34.51 ns 5 0.1221 - 1280 B
'SQLite ADO.NET Insert' 10,076.2 ns 302.63 ns 348.51 ns 6 0.2441 0.0610 2248 B
'GaldrDb Update' 20,401.6 ns 157.08 ns 174.59 ns 7 0.4272 0.0610 3982 B
'GaldrDb Insert' 23,704.4 ns 552.45 ns 567.32 ns 8 0.4272 0.1221 3935 B

SingleOperationBenchmarks

JIT comparison including EF Core. GaldrDb outperforms EF Core across all operations while providing similar developer ergonomics. Note that EF Core Insert has significant per-operation overhead from change tracking and context management.

Method Mean Error StdDev Median Rank Gen0 Gen1 Gen2 Allocated
'GaldrDb Delete' 701.2 ns 174.53 ns 179.23 ns 786.3 ns 1 0.0610 - - 832 B
'GaldrDb Read' 1,625.8 ns 85.68 ns 84.15 ns 1,596.6 ns 2 0.1221 - - 1374 B
'SQLite ADO.NET Delete' 2,781.5 ns 414.43 ns 477.26 ns 2,450.1 ns 3 0.0610 - - 768 B
'SQLite ADO.NET Update' 3,342.9 ns 61.09 ns 62.73 ns 3,334.6 ns 4 0.1221 - - 1200 B
'SQLite ADO.NET Read' 4,786.6 ns 41.29 ns 38.62 ns 4,779.8 ns 5 0.1221 - - 1296 B
'SQLite ADO.NET Insert' 9,872.5 ns 123.61 ns 121.40 ns 9,872.1 ns 6 0.2441 0.0610 - 2264 B
'SQLite EF Core Delete' 16,673.2 ns 160.97 ns 172.24 ns 16,616.0 ns 7 1.4648 0.4883 - 12520 B
'GaldrDb Update' 18,957.7 ns 259.52 ns 288.46 ns 18,943.1 ns 8 0.4272 0.0610 - 4078 B
'SQLite EF Core Read' 20,643.3 ns 341.67 ns 393.47 ns 20,544.7 ns 9 1.4648 0.4883 - 12528 B
'SQLite EF Core Update' 20,906.7 ns 201.73 ns 215.85 ns 20,906.7 ns 9 1.8311 0.6104 - 15704 B
'GaldrDb Insert' 23,214.8 ns 448.47 ns 498.47 ns 23,177.1 ns 10 0.4272 0.1221 - 4055 B
'SQLite EF Core Insert' 1,380,911.3 ns 30,631.48 ns 34,046.81 ns 1,376,880.2 ns 11 366.5161 19.8975 8.7891 3031581 B

Testing

  • 1073 unit and integration tests covering CRUD, transactions, queries, ACID properties, and recovery scenarios
  • 63 deterministic simulation tests for concurrent operations, conflict resolution, and edge cases
  • 1136 total tests across the test suite
  • Stress tests simulating high contention scenarios with concurrent transactions in different workloads
  • Performance benchmarks for single operations, bulk inserts, and query performance

Project Structure

  • GaldrDbEngine/ - Core database engine library
  • GaldrDbAspNetCore/ - ASP.NET Core dependency injection support
  • GaldrDbBrowser/ - Cross-platform database browser for inspecting and editing data
  • GaldrDbConsole/ - Console application for benchmarking, stress testing, and diagnostics
  • GaldrDbSourceGenerators/ - Roslyn source generators for metadata generation
  • Tests/GaldrDb.UnitTests/ - Integration and unit tests
  • Tests/GaldrDb.SimulationTests/ - Deterministic simulation tests for concurrent scenarios
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on GaldrDbEngine:

Package Downloads
GaldrDbAspNetCore

ASP.NET Core dependency injection support for GaldrDb

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-rc2 52 2/12/2026
1.0.0-rc1 56 1/31/2026