Skip to content

Getting Started

Mark Lauter edited this page Aug 10, 2026 · 8 revisions

Getting started

DynamoDbLite is an in-process IAmazonDynamoDB backed by SQLite. Use it as a drop-in replacement for DynamoDB in tests, local development, and offline-capable apps.

Built against AWSSDK.DynamoDBv2 v4 on .NET 10. See the AWS DynamoDB Developer Guide for DynamoDB concepts.

Prerequisites

  • .NET 10 SDK

Installation

dotnet add package MSL.DynamoDbLite

The DI extension (AddDynamoDbLite) ships in the same package — no separate install. The package id is MSL.DynamoDbLite; the assembly and namespace are DynamoDbLite.

Setup

Option 1: Dependency injection

using DynamoDbLite;

builder.Services.AddDynamoDbLite(o =>
    o.WithConnectionString("Data Source=myapp.db"));

AddDynamoDbLite registers IAmazonDynamoDB as a singleton via TryAddSingleton. See DI and configuration for the validation surface, exception types, eager-validation behavior, and notes on IConfiguration binding.

Option 2: Direct instantiation

using DynamoDbLite;

// File-backed (persistent)
using var fileClient = new DynamoDbClient(new DynamoDbLiteOptions(
    "Data Source=myapp.db"));

// In-memory
using var memoryClient = new DynamoDbClient(new DynamoDbLiteOptions(
    $"Data Source=app_{Guid.NewGuid():N};Mode=Memory;Cache=Shared"));

DynamoDbLiteOptions.ConnectionString is required; there is no default. DynamoDbClient implements IDisposable but not IAsyncDisposable, so use using var, not await using. Or let the DI container manage its lifetime.

Storage modes

Mode Connection string Use case
In-memory Data Source=<unique-name>;Mode=Memory;Cache=Shared Unit tests, development
File-backed Data Source=myapp.db Persistent storage, mobile apps

The mode is auto-detected from the connection string: :memory: or Mode=Memory selects an in-memory store; otherwise a file-backed store is created. File-backed stores opt into WAL (write-ahead logging) mode by setting UseWriteAheadLog = true on DynamoDbLiteOptions (or calling WithWriteAheadLog() on the builder). WAL is off by default; see DI and configuration for the contract.

In-memory: name your database

The Data Source name keys SQLite's shared cache. Two clients in the same process with the same name share one in-memory database. For test isolation, suffix the name with a unique value:

$"Data Source=app_{Guid.NewGuid():N};Mode=Memory;Cache=Shared"

Cache=Shared is required for in-memory mode — the library opens a fresh connection per operation, and without it each connection would read an empty database on every call.

In-memory: concurrency and timeouts

Because each operation runs on its own connection, concurrent calls reach the shared database in parallel. Writes need no application-level lock: when two writers collide, the second retries until the first commits — Microsoft.Data.Sqlite drives that retry from the connection's CommandTimeout (default 30 s).

busy_timeout is inert for in-memory stores. Shared-cache write conflicts raise SQLITE_LOCKED_SHAREDCACHE, which SQLite's busy handler does not service, so the pragma changes nothing. The command-timeout retry is what serializes writers. busy_timeout is the contention knob for file-backed stores, where conflicts surface as SQLITE_BUSY. See Concurrency for the full model and WithPragma for the pragma surface.

A complete in-memory registration. The busy_timeout pragma is inert for an in-memory store but harmless, and keeps the configuration correct if the same code later targets a file:

using DynamoDbLite;

builder.Services.AddDynamoDbLite(o => o
    .WithConnectionString($"Data Source=app_{Guid.NewGuid():N};Mode=Memory;Cache=Shared")
    .WithPragma("busy_timeout", "5000"));

Minimal working example

using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using DynamoDbLite;

using var client = new DynamoDbClient(new DynamoDbLiteOptions(
    $"Data Source=app_{Guid.NewGuid():N};Mode=Memory;Cache=Shared"));

// Create a table
await client.CreateTableAsync(new CreateTableRequest
{
    TableName = "Users",
    KeySchema =
    [
        new KeySchemaElement { AttributeName = "UserId", KeyType = KeyType.HASH }
    ],
    AttributeDefinitions =
    [
        new AttributeDefinition { AttributeName = "UserId", AttributeType = ScalarAttributeType.S }
    ],
    ProvisionedThroughput = new ProvisionedThroughput
    {
        // Accepted for API compatibility but not enforced — DynamoDbLite has no capacity limits.
        ReadCapacityUnits = 5,
        WriteCapacityUnits = 5
    }
});

// Put an item
await client.PutItemAsync(new PutItemRequest
{
    TableName = "Users",
    Item = new Dictionary<string, AttributeValue>
    {
        ["UserId"] = new() { S = "user-123" },
        ["Name"] = new() { S = "Alice" },
        ["Age"] = new() { N = "30" }
    }
});

// Get the item
var response = await client.GetItemAsync(new GetItemRequest
{
    TableName = "Users",
    Key = new Dictionary<string, AttributeValue>
    {
        ["UserId"] = new() { S = "user-123" }
    }
});

Console.WriteLine(response.Item["Name"].S); // "Alice"

Configuration reference

DynamoDbLiteOptions is a positional record with one required parameter and one optional flag:

Property Type Description
ConnectionString string SQLite connection string. Required — see Storage modes for the in-memory and file-backed forms.
UseWriteAheadLog bool Defaults to false. Set to true to enable SQLite WAL on file-backed stores. No effect on in-memory stores. See DI and configuration for the full contract.

The connection string follows Microsoft.Data.Sqlite connection string format. The library applies ForeignKeys=true when you leave it unset and otherwise uses the string as written — Mode and Pooling are preserved. Honoring Mode is what lets Mode=Memory select an in-memory store instead of being rewritten to a file.

Next steps

Clone this wiki locally