DejaClient

The shared query cache: a registry of entries keyed by QueryKey, plus invalidation, manual reads/writes, per-prefix defaults, and eviction of idle entries.

public sealed class DejaClient : IDisposable

Registration#

AddDeja (extension)#

public static IServiceCollection AddDeja(this IServiceCollection services, Action<DejaOptions>? configure = null)

Registers DejaOptions (singleton) and DejaClient (Scoped). Scoped is the only correct lifetime across both hosting models: per browser tab on WebAssembly, per user circuit on Blazor Server — a singleton on Server would share one user's cached API responses with every other user. Do not "optimise" this to a singleton.

DejaClient(DejaOptions?)#

public DejaClient(DejaOptions? options = null)

Creates a standalone client (e.g. for tests), optionally with non-default options, and starts the eviction loop.

Reading#

GetData<T>#

public T? GetData<T>(QueryKey key)

The cached data under key, or default when the key is absent, has no data yet, or was stored with a different T.

TryGetData<T>#

public bool TryGetData<T>(QueryKey key, out T? data)

Reads the cached data under key. Returns false when the key is absent or has no data. A key stored with a different T also returns false rather than throwing — it almost always means two call sites disagree about the key's shape, and is reported as a debug-level warning.

GetState#

public CacheEntryState? GetState(QueryKey key)

A read-only snapshot of the entry under key — data presence, fetching flag, invalidation, subscriber count, timestamps — or null when absent. Values are coherent at the moment of the call and do not update afterwards. This site's cache inspectors poll it.

Writing#

SetData<T> (value)#

public void SetData<T>(QueryKey key, T data)

Writes data under key (creating the entry when absent) and notifies every subscribed query. The manual alternative to invalidation when the new value is already known — e.g. appending a mutation result to a cached list. Throws InvalidOperationException when the key holds data of a different type.

SetData<T> (updater)#

public void SetData<T>(QueryKey key, Func<T?, T> updater)

Transforms the cached value under key: updater receives the current data (or default when none) and returns the new value. The building block of the optimistic write pattern.

Invalidation and refetch#

InvalidateAsync#

public Task InvalidateAsync(QueryKey key, InvalidateOptions? options = null)

Marks every entry matching key stale — by prefix unless InvalidateOptions.Exact — and refetches per InvalidateOptions.RefetchType: by default entries with live subscribers refetch in the background (existing data stays on screen, no loading flash), while subscriber-less entries stay marked and refetch when a component next mounts on them. The returned task completes when the triggered refetches settle.

InvalidateAllAsync#

public Task InvalidateAllAsync()

Invalidates every entry in the cache; active entries refetch in the background.

RefetchAsync#

public Task RefetchAsync(QueryKey key, QueryFilter? filter = null)

Refetches every entry matching key that has a fetch function, regardless of staleness. Failures are recorded on the entries, not thrown.

Removal#

Remove#

public void Remove(QueryKey key, QueryFilter? filter = null)

Removes every entry matching key, cancelling in-flight fetches. Subscribed queries keep rendering their last data; their next Execute starts from an empty entry.

Clear#

public void Clear()

Removes every entry, cancelling in-flight fetches.

Defaults and lifecycle#

SetDefaults#

public void SetDefaults(QueryKey prefix, QueryDefaults defaults)

Registers defaults for every key starting with prefix, replacing any previous defaults for the same prefix. Precedence, most specific wins: per-query value > longest matching prefix > DejaOptions — registration order does not matter.

Dispose#

public void Dispose()

Stops the eviction timer and drops every entry; called by DI when the scope ends.

Eviction behaviour#

  • Entries with live subscribers are never evicted.
  • A subscriber-less entry is dropped once its cache time elapses (checked every EvictionInterval).
  • With MaxEntries set, least-recently-used subscriber-less entries are evicted first when the cap is exceeded; the cache may exceed the cap while everything in it is on screen.
  • Executing a query against a key that already holds a different data type throws — two call sites disagreeing about a key's shape is a bug that silent replacement would mask.