Class FileKeyValueStore
IKeyValueStore implementation over an IBlobStore, mapping each key to a single file under a root directory.
Inherited Members
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:
KeyValueStructuredStorekeys look like"{table}/{partitionValue}", which this store turns into one directory per table. - Each raw segment maps to
"{readablePart}-{hash8}", wherehash8is 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 |
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 |