Embedded database for .NET · ACID · concurrent

A real database, inside your app.

An embedded, object-oriented database for .NET. Your data stays in files you can open, diff and commit.

ACID transactions, thousands of concurrent readers and three storage formats, in a library you add with one package. A hundred thousand rows load in about a second; a lookup takes four microseconds.

Native .NET objects Fast reads Concurrent XML · JSON · Binary ACID + crash recovery Apache-2.0
Program.cs
// a database is a directory
using var db = new XodContext("app.xod");

db.Insert(new Customer {
    Name = "Nadia",
    Orders = { new Order { Status = "open" } }
});

// indexed query, not a table scan
var open = db.Query<Order>(o => o.Status == "open")
             .ToList();

Why XOD

An embedded database that behaves like a database.

⌘

Your objects, not a mapping layer

Insert a POCO, query it with a lambda, get the same type back. No SQL strings, no migration files, no generated code — the object model is the schema.

⇄

Many readers, one writer

Per-table locks, queued transactions and a commit journal. Eight threads read at over 140,000 rows a second while a writer keeps committing.

⚡

Fast, and it stays fast

A hundred thousand rows load in about a second, a lookup takes four microseconds, and paging to the end of a large table is instant — measured, not estimated.

▤

Compact by default

A gigabyte of rows fits in about a gigabyte of files, with no compression tricks and no compaction step. What you insert is roughly what you store.

◇

A database per service

It lives in the service process and in its own directory. No server to run, no connection pool to tune, no port to open.

⌁

Runs where servers cannot

Raspberry Pi, industrial PCs and .NET boards. Crash-safe writes when power is cut, and the same engine as the one in your data centre.

Three storage engines

Same code. Different files.

The format is a setting on the context, and XodMigrator converts an existing database without your model assembly. Pick readable, or pick small.

readable pages · default

XML

Plain XML files, one per table page. Open them in an editor, diff them in a pull request, grep them at 2 a.m.

  • Fastest for row reads
  • Human-readable and diffable
  • The default format

readable pages · tool-friendly

JSON

The same page model in JSON, for pipelines that already speak it. Larger on disk than XML and slower on a cold read, but every file opens in any JSON tool.

  • Readable by any JSON tool
  • Larger on disk than XML
  • Good for interchange and inspection

Brotli-compressed · smallest

Binary

Compressed pages: the smallest files XOD can write. Reading a row means decompressing its page, so use it for archives, devices and read-light workloads.

  • Smallest footprint
  • Best for archives and devices
  • Slowest per-row access

Proof

Measured on one machine, published in full.

Four numbers from the current benchmark run: 100,000 rows at 250 MB and 400,000 at 1 GB, on the hardware in the studio. Every run is stamped with the commit that produced it, so the numbers can be checked rather than trusted.

1.18 s
Load 100,000 rows

One transaction, 250 MB of records, index included.

XOD XML

4.5 ms
1,000 lookups by key

About four microseconds per read on a warm database.

XOD XML

1.3 ms
Jump to row 360,000

Deep paging without reading the rows before it.

XOD XML

177,161
Reads per second

Eight threads reading while another writes.

XOD XML

How these numbers were measured →

Get started

Install it like any other package.

One package, one directory, one line of code. There is no server to run, no port to open and no schema migration to apply — the database is created on first use and the files stay yours.

terminal
# add the package to your project
dotnet add package Xod

# run it: the database is a directory
dotnet run
Program.cs
using var db = new XodContext("app.xod");

db.Insert(new Customer {
    Name = "Nadia",
    Orders = { new Order { Status = "open" } }
});

// indexed query, not a table scan
var open = db.Query<Order>(o => o.Status == "open")
             .ToList();
  • Runs on .NET 8 and .NET 9
  • No server, no port, no connection string
  • Data files are yours: inspect, diff, back up with a file copy

Package: nuget.org/packages/Xod · Source: github.com/mhsallam/xod