logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class FileKeyValueStore

    IKeyValueStore implementation over an IBlobStore, mapping each key to a single file under a root directory.

    Inheritance
    object
    FileKeyValueStore
    Implements
    IKeyValueStore
    IKeyedStore
    IPersistenceStore
    Inherited Members
    object.ToString()
    object.Equals(object)
    object.Equals(object, object)
    object.ReferenceEquals(object, object)
    object.GetHashCode()
    object.GetType()
    object.MemberwiseClone()
    Namespace: Serenity.FilePersistence.Infrastructure.Stores
    Assembly: Serenity.Global.Application.dll
    Syntax
    public class FileKeyValueStore : IKeyValueStore, IKeyedStore, IPersistenceStore
    Remarks

    Key-to-path contract (PINNED persistence format — deterministic and injective even on case-insensitive filesystems, so distinct keys can never collide on disk):

    • A forward slash ('/') in a key splits it into path segments, each becoming a directory level. This is deliberate: KeyValueStructuredStore keys look like "{table}/{partitionValue}", which this store turns into one directory per table.
    • Each raw segment maps to "{readablePart}-{hash8}", where hash8 is the first 8 lowercase hex characters of SHA-256 over the raw segment's UTF-8 bytes. The hash — not the readable part — carries injectivity: keys differing only in letter case ("Alpha" vs "alpha") hash differently, so they cannot collide on Windows/macOS case-insensitive filesystems, and a segment whose readable part would equal a Windows reserved device name (con, prn, aux, nul, com1-9, lpt1-9) never yields that reserved basename because the hash suffix is always appended.
    • The readable part exists for debuggability only: every UTF-8 byte of the raw segment outside the unreserved set [A-Za-z0-9_-] — including '%', '.', and any non-ASCII byte — is percent-encoded as %XX (uppercase hex of the byte value), then the result is truncated to at most 64 characters (safe because injectivity lives in the hash).
    • Because '.' is always percent-encoded, a literal ".." path segment can never be produced by a sanitized key, so a key cannot escape the configured root directory.

    The resulting path is {rootDirectory}/{segment1}/{segment2}/...{segmentN}{fileExtension}, where fileExtension defaults to DEFAULT_FILE_EXTENSION (".bin") but can be overridden by the constructor to match whatever format the caller's serializer produces.

    Constructors

    FileKeyValueStore(IBlobStore, string, string, string)

    Initializes a new instance of the FileKeyValueStore class.

    Declaration
    public FileKeyValueStore(IBlobStore blobStore, string rootDirectory, string nameSpace = "file-key-value", string fileExtension = ".bin")
    Parameters
    Type Name Description
    IBlobStore blobStore

    Blob store used to persist each key as an individual file.

    string rootDirectory

    Directory every key is stored under.

    string nameSpace

    Logical namespace for diagnostics/telemetry. Defaults to DEFAULT_NAMESPACE.

    string fileExtension

    Extension appended to every stored file, including its leading dot. Defaults to DEFAULT_FILE_EXTENSION; callers whose serializer produces a different format (e.g. human-readable JSON) can pass a matching extension such as ".json" so the files on disk advertise their real content.

    Fields

    DEFAULT_FILE_EXTENSION

    Default per-key file extension, used when no fileExtension constructor argument is supplied.

    Declaration
    public const string DEFAULT_FILE_EXTENSION = ".bin"
    Field Value
    Type Description
    string

    DEFAULT_NAMESPACE

    Default logical namespace identifying this store as file-based key-value persistence.

    Declaration
    public const string DEFAULT_NAMESPACE = "file-key-value"
    Field Value
    Type Description
    string

    Properties

    Namespace

    Declaration
    public string Namespace { get; }
    Property Value
    Type Description
    string

    Methods

    DeleteAsync(string, CancellationToken)

    Declaration
    public virtual Task DeleteAsync(string key, CancellationToken cancellationToken)
    Parameters
    Type Name Description
    string key
    CancellationToken cancellationToken
    Returns
    Type Description
    Task

    ExistsAsync(string, CancellationToken)

    Declaration
    public virtual Task<bool> ExistsAsync(string key, CancellationToken cancellationToken)
    Parameters
    Type Name Description
    string key
    CancellationToken cancellationToken
    Returns
    Type Description
    Task<bool>

    GetAsync(string, CancellationToken)

    Declaration
    public virtual Task<ReadOnlyMemory<byte>?> GetAsync(string key, CancellationToken cancellationToken)
    Parameters
    Type Name Description
    string key
    CancellationToken cancellationToken
    Returns
    Type Description
    Task<ReadOnlyMemory<byte>?>
    Remarks

    A missing key returns null, matching Serenity.PlayerPrefsPersistence.Infrastructure.Stores.UnityPlayerPrefsKeyValueStore. An existing-but-unreadable/corrupt file is NOT swallowed here — it propagates, matching that same sibling (its Base64 decode throws uncaught on corrupt data). Callers such as KeyValueStructuredStore already wrap every GetAsync(string, CancellationToken) call in their own try/catch, so double-swallowing here would only hide the real failure reason.

    HealthCheckAsync(CancellationToken)

    Declaration
    public virtual Task<bool> HealthCheckAsync(CancellationToken cancellationToken)
    Parameters
    Type Name Description
    CancellationToken cancellationToken
    Returns
    Type Description
    Task<bool>

    SetAsync(string, ReadOnlyMemory<byte>, CancellationToken)

    Declaration
    public virtual Task SetAsync(string key, ReadOnlyMemory<byte> value, CancellationToken cancellationToken)
    Parameters
    Type Name Description
    string key
    ReadOnlyMemory<byte> value
    CancellationToken cancellationToken
    Returns
    Type Description
    Task

    Implements

    IKeyValueStore
    IKeyedStore
    IPersistenceStore
    In this article
    © 2026 Serenity. All Rights Reserved