PhoenixmlDb.Core

IDocumentDatabase

Root entry point for all PhoenixmlDb operations.

#IDocumentDatabase

Namespace: PhoenixmlDb.Core

Root entry point for all PhoenixmlDb operations.

IDocumentDatabase is the first object you create when working with PhoenixmlDb. It manages the underlying LMDB storage engine and provides access to containers, which hold collections of XML and JSON documents.

A database maps to a single directory on disk. All containers and their documents are stored within this directory using LMDB's memory-mapped file architecture, which provides excellent read performance and ACID transaction guarantees.

Basic usage:

csharp
await using var db = await DocumentDatabase.OpenAsync("/path/to/db");
var container = await db.OpenOrCreateContainerAsync("products", opts =>
    opts.Indexes.AddPathIndex("//product/name")
                .AddValueIndex("//product/price", XdmValueType.XdmDecimal));
await container.PutDocumentAsync("item1.xml", xmlContent);
                                  

Thread safety: An IDocumentDatabase instance is thread-safe. Multiple threads can read concurrently. Write transactions are serialized β€” only one write transaction can be active at a time, consistent with LMDB's single-writer model.

Disposal: Always dispose the database when done. This flushes pending writes and releases the LMDB environment. Use await using for automatic disposal.

#Properties

Name Description
Path Gets the filesystem path where this database is stored.
Statistics Gets current database statistics including size, container count, and document count.

#Methods

#BeginRead

Begins a read-only transaction providing snapshot isolation.

Returns: A read transaction that sees a consistent snapshot of the database.

Read transactions provide MVCC (Multi-Version Concurrency Control) snapshot isolation. Once begun, the transaction sees a frozen view of the database β€” concurrent writes by other threads or processes are invisible. This guarantees consistent reads without locking.

Multiple read transactions can be active simultaneously. Read transactions are lightweight and do not block writers.

Important: Always dispose read transactions promptly. Long-lived read transactions prevent LMDB from reclaiming disk space used by older versions of data.

For simple single-operation reads, the IContainer methods (e.g., GetDocumentAsync) handle transactions automatically. Use explicit transactions when you need to read multiple documents with a consistent view.

#BeginWriteAsync(Threading.CancellationToken)

Begins a read-write transaction, waiting indefinitely for the write lock.

Parameters:

  • cancellationToken β€” Cancellation token.

Returns: A write transaction. Call CommitAsync() to persist changes or RollbackAsync() to discard them.

Only one write transaction can be active at a time across all threads and processes accessing the same database. If another write transaction is active, this method blocks until it completes (commits or rolls back).

Use the overload with TimeSpan timeout to avoid indefinite blocking.

Write transactions also provide read access β€” you can query documents within the same transaction to implement read-modify-write patterns.

#BeginWriteAsync(TimeSpan,Threading.CancellationToken)

Begins a read-write transaction with a timeout for acquiring the write lock.

Parameters:

  • timeout β€” Maximum time to wait for the write lock.

  • cancellationToken β€” Cancellation token.

Returns: A write transaction.

Exceptions:

Prefer this overload in production code to prevent threads from blocking indefinitely. A timeout of 5-30 seconds is typical for most applications.

#CreateContainerAsync(String,Action<PhoenixmlDb.Core.ContainerOptions>,Threading.CancellationToken)

Creates a new container with the specified name and optional configuration.

Parameters:

  • name β€” Container name. Must be unique within the database. Use meaningful names that describe the document collection, e.g. "orders", "customers", "config".

  • configure β€” Optional configuration action to set up indexes, default namespaces, and validation mode. If null, the container is created with default settings (no indexes). Indexes can be added later, but existing documents won't be retroactively indexed.

  • cancellationToken β€” Cancellation token.

Returns: The newly created container, ready for document operations.

Exceptions:

Example:

csharp
var products = await db.CreateContainerAsync("products", opts =>
{
    opts.Indexes
        .AddPathIndex("//product/@id")
        .AddValueIndex("//product/price", XdmValueType.XdmDecimal)
        .AddFullTextIndex("//product/description");
    opts.DefaultNamespaces.Add("p", "http://example.com/products");
});
                                  

#DeleteContainerAsync(String,Threading.CancellationToken)

Permanently deletes a container and all of its documents, indexes, and metadata.

Parameters:

  • name β€” Container name.

  • cancellationToken β€” Cancellation token.

Returns: true if the container existed and was deleted; false if it didn't exist.

This operation is irreversible.

All documents, metadata, and index data within the container are permanently removed. The disk space is reclaimed by LMDB for future use.

#FlushAsync(Threading.CancellationToken)

Forces any buffered writes to be flushed to persistent storage.

Parameters:

  • cancellationToken β€” Cancellation token.

LMDB normally syncs data to disk on transaction commit. Call this method if you need to guarantee durability at a specific point, for example before reporting success to a client.

#ListContainersAsync(Threading.CancellationToken)

Lists all containers in the database.

Parameters:

  • cancellationToken β€” Cancellation token.

Returns: An async enumerable of ContainerInfo records with container metadata including name, creation date, and document count.

#OpenContainerAsync(String,Threading.CancellationToken)

Opens an existing container by name.

Parameters:

  • name β€” Container name.

  • cancellationToken β€” Cancellation token.

Returns: The container, or null if no container with this name exists.

This is the preferred method when you know the container should exist and want to handle the not-found case explicitly. For fire-and-forget scenarios, use

.

#OpenOrCreateContainerAsync(String,Action<PhoenixmlDb.Core.ContainerOptions>,Threading.CancellationToken)

Opens a container, creating it if it doesn't already exist.

Parameters:

  • name β€” Container name.

  • configure β€” Configuration action applied only when creating a new container. Ignored if the container already exists.

  • cancellationToken β€” Cancellation token.

Returns: The existing or newly created container.

This is the most convenient method for application startup β€” call it without worrying about whether the container exists yet. Note that the

action is only applied on creation; it won't modify an existing container's settings.