PhoenixmlDb.Core

IContainer

Primary interaction surface for storing, retrieving, querying, and managing documents within a PhoenixmlDb container.

#IContainer

Namespace: PhoenixmlDb.Core

Primary interaction surface for storing, retrieving, querying, and managing documents within a PhoenixmlDb container.

IContainer is the interface most application code works with day-to-day. While IDocumentDatabase manages containers and transactions at the database level, IContainer is where you put documents, run XQuery queries, and manage metadata.

A container holds a collection of documents that share the same index configuration, namespace bindings, and validation rules. Documents can be either XML or JSON β€” both formats are stored natively, parsed into the XQuery Data Model (XDM), and are fully queryable via XQuery.

Document naming: Each document is identified by a name string within its container. Names are URI-like identifiers. Path-style names (e.g., "customers/acme/profile.xml") are recommended because they enable prefix-based listing via IContainer.ListDocumentsAsync and create a natural organizational hierarchy. Names are case-sensitive and must be unique within the container.

Convenience methods vs. explicit transactions: The methods on IContainer (e.g., IContainer.PutDocumentAsync, IContainer.GetDocumentAsync) each run within an implicit transaction β€” a write transaction for mutations, a read transaction for queries. This is convenient for single operations, but if you need to perform multiple reads or writes atomically, use IDocumentDatabase.BeginWriteAsync or IDocumentDatabase.BeginRead to create an explicit transaction instead.

Basic usage:

csharp
// Store a document
await container.PutDocumentAsync("orders/2024/order-001.xml", orderXml);

// Retrieve it
var doc = await container.GetDocumentAsync("orders/2024/order-001.xml");
var content = await doc!.GetContentAsync();

// Query with XQuery
await foreach (var result in container.QueryAsync("//order[total > 100]"))
{
    Console.WriteLine(result);
}

// List documents by prefix
await foreach (var info in container.ListDocumentsAsync("orders/2024/"))
{
    Console.WriteLine($"{info.Name} ({info.SizeBytes} bytes)");
}
                                  

#Properties

Name Description
Id Gets the unique identifier for this container within the database.
Name Gets the human-readable name of this container.
Options Gets the configuration options for this container, including indexes, namespace bindings, and validation settings.

#Methods

#DeleteDocumentAsync(String,Threading.CancellationToken)

Permanently deletes a document from the container.

Parameters:

  • name β€” The name of the document to delete.

  • cancellationToken β€” Cancellation token.

Returns: true if the document existed and was deleted; false if no document with the given name was found.

Deletion removes the document content, its XDM node tree, all associated index entries, and all metadata. This operation is irreversible.

This method runs within an implicit write transaction. The return value lets you distinguish between "deleted successfully" and "nothing to delete" without needing a separate IContainer.DocumentExistsAsync call.

Example:

csharp
bool wasDeleted = await container.DeleteDocumentAsync("obsolete/old-report.xml");
if (!wasDeleted)
    Console.WriteLine("Document was already gone.");
                                  

#DocumentExistsAsync(String,Threading.CancellationToken)

Checks whether a document with the specified name exists in this container.

Parameters:

  • name β€” The document name to check (case-sensitive).

  • cancellationToken β€” Cancellation token.

Returns: true if the document exists; false otherwise.

This is a lightweight existence check that does not load the document content. Use it for guard checks before operations where you want to handle the exists/not-exists cases differently.

Note that in concurrent scenarios, the document could be created or deleted between this check and a subsequent operation. For atomic "create if not exists" behavior, use IContainer.PutDocumentAsync with Overwrite = false and catch DocumentExistsException.

#GetAllMetadataAsync(String,Threading.CancellationToken)

All metadata for a document, keyed by qualified name.

Parameters:

  • documentName β€” The document to read from.

  • cancellationToken β€” Cancels the operation.

Returns: The document's metadata; empty if it has none.

Example:

csharp
var all = await container.GetAllMetadataAsync("reports/q1.xml");
var creator = all.Get(DcTerms.Creator);
                                  

#GetDocumentAsync(String,Threading.CancellationToken)

Retrieves a document by its name.

Parameters:

  • name β€” The exact document name to look up (case-sensitive).

  • cancellationToken β€” Cancellation token.

Returns: The IDocument if found, or null if no document with the given name exists in this container.

The returned IDocument provides access to the document's content (via IDocument.GetContentAsync or IDocument.GetContentStreamAsync), its parsed XDM tree (via IDocument.GetRootNodeAsync), and its metadata.

This method runs within an implicit read transaction. If you need to read multiple documents with a consistent snapshot, use IDocumentDatabase.BeginRead to create an explicit read transaction.

Example:

csharp
var doc = await container.GetDocumentAsync("orders/order-001.xml");
if (doc is not null)
{
    var xml = await doc.GetContentAsync();
    Console.WriteLine($"Document {doc.Name}, {doc.SizeBytes} bytes, type: {doc.ContentType}");
}
                                  

#GetMetadataAsync(String,PhoenixmlDb.Xdm.XdmQName,Threading.CancellationToken)

Reads a metadata value by explicit qualified name.

Parameters:

  • documentName β€” The document to read from.

  • name β€” The fully qualified metadata name.

  • cancellationToken β€” Cancels the operation.

Returns: The value, or null if the document has no such metadata.

#GetMetadataAsync(String,String,Threading.CancellationToken)

Reads a metadata value from the container's default namespace.

Parameters:

  • documentName β€” The document to read from.

  • name β€” The local metadata name, resolved against the container's default namespace.

  • cancellationToken β€” Cancels the operation.

Returns: The value, or null if the document has no such metadata.

#GetMetadataAsync``1(String,PhoenixmlDb.Core.Metadata.MetadataProperty<``0>,Threading.CancellationToken)

Reads a typed metadata value.

Type parameters:

  • T β€” The property's CLR value type.

Parameters:

  • documentName β€” The document to read from.

  • descriptor β€” The metadata property descriptor.

  • cancellationToken β€” Cancels the operation.

Returns: The value, or default if the document has no such metadata.

#GetMetadataByNamespaceAsync(String,PhoenixmlDb.Core.NamespaceId,Threading.CancellationToken)

All metadata for a document within one namespace.

Parameters:

  • documentName β€” The document to read from.

  • namespaceId β€” The namespace to restrict to.

  • cancellationToken β€” Cancels the operation.

Returns: The matching metadata; empty if the document has none in that namespace.

Served by a cursor range over the namespace key prefix, not by filtering.

#ListDocumentsAsync(String,Threading.CancellationToken)

Lists documents whose names start with the specified prefix.

Parameters:

  • prefix β€” The name prefix to filter by. For example, "orders/2024/" returns all documents whose names begin with that string. The match is case-sensitive.

  • cancellationToken β€” Cancellation token.

Returns: An IAsyncEnumerable`1 of DocumentInfo records for documents matching the prefix.

This is why path-style document names are recommended β€” they enable efficient hierarchical browsing. For example, with documents named "orders/2024/q1/inv-001.xml", "orders/2024/q2/inv-042.xml", etc., you can list all 2024 orders with prefix "orders/2024/" or just Q1 with "orders/2024/q1/".

The prefix match is performed on the stored name index and is efficient even for containers with many documents.

Example:

csharp
// List all configuration documents
await foreach (var info in container.ListDocumentsAsync("config/"))
{
    Console.WriteLine(info.Name);
}
                                  

#ListDocumentsAsync(Threading.CancellationToken)

Lists all documents in the container.

Parameters:

  • cancellationToken β€” Cancellation token.

Returns: An IAsyncEnumerable`1 of DocumentInfo records, one per document. The enumeration order is implementation-defined.

Each DocumentInfo contains lightweight metadata (name, size, timestamps, content type) without loading the full document content. This is efficient for building document inventories, dashboards, or migration scripts.

For large containers, consider using the prefix-based overload IContainer.ListDocumentsAsync to narrow results.

Example:

csharp
await foreach (var info in container.ListDocumentsAsync())
{
    Console.WriteLine($"{info.Name} β€” {info.ContentType}, {info.SizeBytes} bytes");
}
                                  

#PutDocumentAsync(String,IO.Stream,PhoenixmlDb.Core.DocumentOptions,Threading.CancellationToken)

Stores a document in the container from a

.

Parameters:

  • name β€” Document name (URI-like identifier). See IContainer.PutDocumentAsync for naming conventions.

  • content β€” A readable stream containing XML or JSON content. The stream is read to completion but is not disposed by this method β€” the caller retains ownership.

  • options β€” Optional settings controlling content type detection, overwrite behavior, and initial metadata. See DocumentOptions for details.

  • cancellationToken β€” Cancellation token.

Returns: A ValueTask that completes when the document is stored and indexed.

Exceptions:

Use this overload when loading documents from files, HTTP responses, or other stream-based sources to avoid buffering the entire content in memory as a string. This is especially beneficial for large documents.

Example:

csharp
await using var fileStream = File.OpenRead("/data/catalog.xml");
await container.PutDocumentAsync("catalog.xml", fileStream);
                                  

#PutDocumentAsync(String,String,PhoenixmlDb.Core.DocumentOptions,Threading.CancellationToken)

Stores a document in the container from a string.

Parameters:

  • name β€” Document name (URI-like identifier). Path-style names are recommended (e.g., "invoices/2024/inv-1042.xml") to enable prefix-based listing. Names are case-sensitive and must be unique within the container.

  • content β€” The document content as XML or JSON. The content type is auto-detected from the content itself unless explicitly specified via options. The content must be well-formed XML or valid JSON.

  • options β€” Optional settings controlling content type detection, overwrite behavior, and initial metadata. See DocumentOptions for details.

  • cancellationToken β€” Cancellation token.

Returns: A ValueTask that completes when the document is stored and indexed.

Exceptions:

This method runs within an implicit write transaction. The document is parsed, validated (if ContainerOptions.ValidationMode is set), stored, and indexed atomically.

By default, DocumentOptions.Overwrite is true, so calling this method with an existing document name silently replaces the previous content. Set Overwrite = false when you want insert-only semantics (e.g., to prevent accidental data loss).

Example:

Store an XML document with default options (auto-detect, overwrite enabled):

csharp
await container.PutDocumentAsync("products/widget.xml",
    "<product><name>Widget</name><price>9.99</price></product>");
                                  

Store a JSON document with metadata and insert-only semantics:

csharp
await container.PutDocumentAsync("events/evt-42.json",
    """{"type": "click", "timestamp": "2024-03-15T10:30:00Z"}""",
    new DocumentOptions
    {
        ContentType = ContentType.Json,
        Overwrite = false,
        Metadata = new Dictionary<XdmQName, XdmValue>
        {
            [new XdmQName(NamespaceId.PhoenixmlMeta, "source")] = XdmValue.From("web"),
            [new XdmQName(NamespaceId.PhoenixmlMeta, "priority")] = XdmValue.From(1L)
        }
    });
                                  

#PutDocumentsAsync(Collections.Generic.IEnumerable<PhoenixmlDb.Core.DocumentInput>,Threading.CancellationToken)

Writes multiple documents. Engine implementations batch them into a single write transaction (one commit) for bulk-load throughput; this default falls back to sequential single-document writes so other implementations keep working. Returns the number of documents written.

#QueryAsync(String,Collections.Generic.IReadOnlyDictionary<String,Object>,Predicate<String>,Threading.CancellationToken)

Executes

against documents whose name passes

. Use this to hide system / sidecar documents from user-facing query surfaces. Defaults to delegating to the unfiltered overload so existing implementations stay source-compatible.

Parameters:

  • query β€” XQuery 4.0 expression.

  • variables β€” External variable bindings.

  • documentNameFilter β€” Predicate over document name. Documents for which it returns false are excluded before query iteration. Pass null for legacy unfiltered behavior.

  • cancellationToken β€” Cancellation token.

#QueryAsync(String,Collections.Generic.IReadOnlyDictionary<String,Object>,Threading.CancellationToken)

Executes an XQuery expression against the documents in this container.

Parameters:

  • query β€” An XQuery expression. The expression runs in the context of this container's document collection. Use collection() to access all documents, or doc("name") to access a specific document by name.

  • variables β€” Optional external variable bindings. Keys are variable names (without the $ prefix); values are the variable values. These can be referenced in the query as $variableName.

  • cancellationToken β€” Cancellation token.

Returns: An IAsyncEnumerable`1 yielding each item in the XQuery result sequence. Results may be XDM nodes, atomic values, or other XQuery items depending on the query.

Queries benefit from the indexes configured in ContainerOptions.Indexes. For example, a path index on "//product/price" accelerates queries like //product[price > 50]. Without appropriate indexes, queries perform full document scans.

The default namespace bindings from ContainerOptions.DefaultNamespaces are automatically available in query expressions, so you don't need to redeclare them.

External variables are useful for parameterizing queries safely, avoiding string concatenation that could lead to injection issues.

Example:

Simple query across all documents:

csharp
await foreach (var result in container.QueryAsync("collection()//product[price > 100]"))
{
    Console.WriteLine(result);
}
                                  

Parameterized query with external variables:

csharp
var variables = new Dictionary<string, object>
{
    ["minPrice"] = 50.0m,
    ["category"] = "electronics"
};
await foreach (var result in container.QueryAsync(
    """
    for $p in collection()//product
    where $p/price > $minPrice and $p/category = $category
    order by $p/price descending
    return $p/name
    """,
    variables))
{
    Console.WriteLine(result);
}
                                  

#QueryMetadataAsync(PhoenixmlDb.Xdm.XdmQName,PhoenixmlDb.Xdm.XdmValue,Threading.CancellationToken)

Streams documents whose metadata equals a value, by explicit qualified name.

Parameters:

  • name β€” The fully qualified metadata name to match on.

  • value β€” The value to match.

  • cancellationToken β€” Cancels the enumeration.

#QueryMetadataAsync``1(PhoenixmlDb.Core.Metadata.MetadataProperty<``0>,``0,Threading.CancellationToken)

Streams documents whose metadata property equals a value.

Type parameters:

  • T β€” The property's CLR value type.

Parameters:

  • descriptor β€” The metadata property to match on.

  • value β€” The value to match.

  • cancellationToken β€” Cancels the enumeration.

Most efficient when a metadata index is configured for the property via

. Without one the query scans the container's documents; both paths use the same value encoding and return the same documents.

Example:

csharp
await foreach (var info in container.QueryMetadataAsync(Routing.Status, "approved"))
    Console.WriteLine($"Approved: {info.Name}");
                                  

#QueryMetadataRangeAsync(PhoenixmlDb.Xdm.XdmQName,Nullable<PhoenixmlDb.Xdm.XdmValue>,Nullable<PhoenixmlDb.Xdm.XdmValue>,Boolean,Boolean,Threading.CancellationToken)

Streams documents whose metadata falls within a range, by explicit qualified name.

Parameters:

  • name β€” The fully qualified metadata name to range over.

  • lowerBound β€” Lower bound, or null for unbounded below.

  • upperBound β€” Upper bound, or null for unbounded above.

  • lowerInclusive β€” Whether lowerBound itself matches.

  • upperInclusive β€” Whether upperBound itself matches.

  • cancellationToken β€” Cancels the enumeration.

The general form. Use it for reference-typed properties such as

MetadataProperty<string>

, which the typed overload cannot express: an unconstrained

T?

cannot represent "no bound" for a value type, and using

default(T)

for it would make an unbounded range indistinguishable from one bounded at

DateTimeOffset.MinValue

or

0

.

#QueryMetadataRangeAsync``1(PhoenixmlDb.Core.Metadata.MetadataProperty<``0>,Nullable<``0>,Nullable<``0>,Boolean,Boolean,Threading.CancellationToken)

Streams documents whose metadata property falls within a range of values.

Type parameters:

  • T β€” The property's CLR value type.

Parameters:

  • descriptor β€” The metadata property to range over.

  • lowerBound β€” Lower bound, or null for unbounded below.

  • upperBound β€” Upper bound, or null for unbounded above.

  • lowerInclusive β€” Whether lowerBound itself matches. Defaults to true.

  • upperInclusive β€” Whether upperBound itself matches. Defaults to true.

  • cancellationToken β€” Cancels the enumeration.

Either bound may be null for an open-ended range: from: cutoff, to: null reads "at or after cutoff". Both null matches every document that carries the property at all.

A metadata index declared for this property with a matching value type serves the range directly; otherwise the container is scanned. As with equality, both paths return the same documents β€” the index changes how the answer is found, not what it is.

Ordering follows the property's XDM type, not its .NET string form: dates order chronologically and numbers numerically.

Example:

csharp
// everything received on or after the cutoff
await foreach (var d in container.QueryMetadataRangeAsync(Received, lowerBound: cutoff, upperBound: null))
    Console.WriteLine(d.Name);
                                  

#SetMetadataAsync(String,PhoenixmlDb.Xdm.XdmQName,PhoenixmlDb.Xdm.XdmValue,Threading.CancellationToken)

Sets a metadata value by explicit qualified name.

Parameters:

  • documentName β€” The document to annotate.

  • name β€” The fully qualified metadata name.

  • value β€” The value to store.

  • cancellationToken β€” Cancels the operation.

Exceptions:

Opens and commits its own write transaction; see the string overload.

#SetMetadataAsync(String,String,String,Threading.CancellationToken)

Sets a metadata value in the container's default namespace.

Parameters:

  • documentName β€” The document to annotate.

  • name β€” The local metadata name, resolved against the container's default namespace.

  • value β€” The value to store.

  • cancellationToken β€” Cancels the operation.

Exceptions:

This overload opens and commits its own write transaction. To make a metadata write part of a larger atomic unit, use IWriteTransaction.SetMetadataAsync``1.

name is a local name, resolved against the container's ContainerOptions.DefaultMetadataNamespace. Two applications sharing a database can therefore both use a common name such as status without colliding.

To make metadata queryable with IContainer.QueryMetadataAsync``1, add a metadata index to the container's IndexConfiguration via IndexConfiguration.AddMetadataIndex.

Example:

csharp
await container.SetMetadataAsync("reports/q1.xml", "status", "approved");
                                  

#SetMetadataAsync``1(String,PhoenixmlDb.Core.Metadata.MetadataProperty<``0>,``0,Threading.CancellationToken)

Sets a typed metadata value. The namespace and value type come from the property.

Type parameters:

  • T β€” The property's CLR value type.

Parameters:

  • documentName β€” The document to annotate.

  • descriptor β€” The metadata property descriptor.

  • value β€” The value to store.

  • cancellationToken β€” Cancels the operation.

Exceptions:

Opens and commits its own write transaction; see the string overload.

Example:

csharp
await container.SetMetadataAsync("reports/q1.xml", DcTerms.Creator, "Jane Smith");