Getting Started

Getting Started

Requirements

MemDb targets .NET 9 or later and ships as a single assembly with no external runtime dependencies.

Installation

dotnet add package HatTrick.MemDb

The package is HatTrick.MemDb; the namespace is HatTrick.Data:

using HatTrick.Data;

Configuration

MemDb requires a dataset name. Providing a directory path enables file-backed persistence; omitting the path runs the dataset purely in RAM (see Modes).

MemDb.ConfigureFor<Person>("people", @"C:\data\people")
     .Register();

ConfigureFor returns a fluent builder; call .Register() when complete. Configure once at startup (or DI registration), before any MemDb.Open call. Configuration is not persisted — serializer, encryption key, indexes, and other settings must be re-supplied on every application start.

Inspecting and removing configuration

Two static helpers manage the in-process configuration registry:

// Test whether a dataset name has been registered (case-insensitive).
bool registered = MemDb.ContainsConfigurationFor("people");

// Remove a previously registered configuration. The dataset must not be open.
MemDb.RemoveConfigurationFor("people");

RemoveConfigurationFor throws ArgumentException if the name is not registered and InvalidOperationException if a MemDb<T> instance for the dataset is open. ContainsConfigurationFor guards against duplicate registration in DI setups that may run more than once.

A complete first program

using System;
using HatTrick.Data;

public class Person
{
    public long Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

class Program
{
    static void Main()
    {
        // Configure — once at startup, before any Open calls.
        MemDb.ConfigureFor<Person>("people", @"C:\data\people")
             .Register();

        // Open — dispose flushes pending writes and releases file handles.
        using (var db = MemDb.Open<Person>("people"))
        {
            var person = new Person
            {
                FirstName = "Eric",
                LastName = "Cartman",
                BirthDate = new DateTime(2014, 5, 13)
            };

            // Insert — id auto-assigned and captured via callback.
            db.Insert(person, id => person.Id = id);

            // Update — by id.
            db.Update(p => p.MiddleName = "Theodore", person.Id);

            // Find — returns a deep clone, never a cache reference.
            var found = db.Find(person.Id);
            Console.WriteLine($"{found.FirstName} {found.MiddleName} {found.LastName}");
        }
    }
}

Expected output:

Eric Theodore Cartman

Next steps

  • Core Concepts — operational modes, persistence model, flush semantics, and concurrency guarantees
  • Architecture — how the engine is wired internally and how data moves between cache and disk
  • Basic Operations — the full read/write API
  • Queries — the fluent query builder for filtering, sorting, aggregation, and query-scoped writes