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:
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:
-
TransactionTimeoutExceptionβ The write lock could not be acquired within the specified timeout.
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:
-
InvalidOperationExceptionβ A container with this name already exists. UseIDocumentDatabase.OpenOrCreateContainerAsyncif you want create-if-not-exists semantics.
Example:
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.