Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! Learn More

Stores Release Notes

Fix
StoreExtensions.Get<T>(store, key, defaultValue) now checks Contains(key) before reading, so a stored 0, false, or default-enum value is no longer mistaken for a missing key and replaced with the supplied default
Enhancement
Shiny.Stores.Default and Shiny.Stores.Secure are now self-bootstrapping. On mobile/desktop they lazily construct the platform-native store on first access — no post-build UseShinyStores() call is required. Generated [Bind] property accessors will work even if read during host construction (fixes InvalidOperationException: Shiny stores not initialized for code paths that touch settings before the post-build init ran)
Feature
New Shiny.Stores.Register(object key, IKeyValueStore store) — registers or overrides a store for any key. Use for tests, custom store keys referenced by [Bind("custom-key")], or scenarios where the store can’t be constructed statically (Blazor IJSRuntime-backed stores)
Feature
New Shiny.Stores.Serializer — single shared DefaultSerializer instance used by both the static accessor and DI. services.AddJsonContext(...) now installs its context onto this same instance, so static and DI consumers can’t drift
Feature
New Shiny.Stores.Reset() — clears all static registrations and forces re-bootstrap on next access (useful for test isolation)
Enhancement
UseShinyStores() is now optional on mobile/desktop. It remains useful for Blazor (where LocalStorageKeyValueStore needs IJSRuntime from the built provider) and for snapshotting keyed IKeyValueStore registrations from DI into the static accessor
BREAKING Enhancement
IKeyValueStore has been reshaped to a generic, AOT-friendly API. The Alias property is gone; object? Get(Type, string) and Set(string, object) have been replaced by T? Get<T>(string) and Set<T>(string, T)
BREAKING Enhancement
ISerializer is now generic-first (T Deserialize<T> / string Serialize<T>). Runtime-typed overloads object? Deserialize(Type, string) and string Serialize(object, Type?) remain for the object binder
BREAKING Enhancement
DefaultSerializer is now AOT-clean and strict — types must be supplied via a JsonSerializerContext. Register one with services.AddJsonContext(MyJsonContext.Default). There is no longer a reflection-based fallback by default; for non-AOT scenarios (e.g. unit tests) add serializer.Options.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver()) explicitly
Feature
New StoreKeys constants (StoreKeys.Default = "settings", StoreKeys.Secure = "secure") — stores are now registered as keyed singletons in DI instead of by Alias. Resolve with sp.GetRequiredKeyedService<IKeyValueStore>(StoreKeys.Default)
BREAKING Enhancement
IKeyValueStoreFactory and KeyValueStoreFactory have been removed. Use keyed DI lookups against StoreKeys (or your own keys) instead of factory.GetStore(alias)
BREAKING Enhancement
IObjectStoreBinder.Bind now takes object? storeKey (a DI service key) instead of string? keyValueStoreAlias. The implementation now depends on IServiceProvider + ISerializer, not IKeyValueStoreFactory
BREAKING Enhancement
ObjectStoreBinderAttribute.StoreAlias has been renamed to StoreKey
BREAKING Enhancement
AddPersistentService<TImpl>(string? alias) is now AddPersistentService<TImpl>(Func<IServiceProvider, TImpl> factory, object? storeKey = null). A factory is required to keep registration AOT-clean. The non-generic AddPersistentService(Type, ...) overload has been removed
Feature
New static Shiny.Stores accessor — Stores.Default, Stores.Secure, and Stores.Keyed(key) resolve the registered IKeyValueStore for the corresponding service key. Initialize once after the service provider is built by calling serviceProvider.UseShinyStores() (or the lower-level Stores.Initialize(serviceProvider))
Feature
Object binding is now source-generated via the new [Bind] attribute (in Shiny.Extensions.DependencyInjection). The DI source generator emits partial property bodies that call Stores.Default.Set/Get (or Stores.Secure, Stores.Keyed(...)). No INotifyPropertyChanged required, no runtime reflection, fully AOT-clean
BREAKING Enhancement
Removed IObjectStoreBinder / ObjectStoreBinder, ObjectStoreBinderAttribute, BindOnResolve(), and AddPersistentService<T>(). The runtime INPC-based binder has been replaced by source-generated [Bind] properties — migrate by marking your settings class partial, declaring [Bind] partial properties, and (optionally) marking the class [Singleton] for auto-registration
Feature
New services.AddJsonContext(JsonSerializerContext) extension — installs (or chains to) the shared DefaultSerializer and registers types for AOT-safe serialization
Feature
Repositories have been folded into Shiny.Extensions.Stores. New IRepository + IRepositoryEntity API with the FileSystemRepository implementation (transactional, in-memory-cached, {EntityName}_{Id}.shiny files), RepositoryAction change-event enum, RepositoryException, and services.AddDefaultRepository(DirectoryInfo? rootDir = null) registration helper
Feature Blazor WebAssembly
New LocalStorageRepository — mirrors FileSystemRepository over the browser’s localStorage via shiny:repo:{EntityName}: prefixed keys. Register with services.AddLocalStorageRepository()
BREAKING Enhancement Blazor WebAssembly
LocalStorageKeyValueStore has been rewritten to use ISerializer and prefix-namespaced keys (shiny:kvs:settings:). Register with services.AddLocalStorageKeyValueStore() — registers under StoreKeys.Default and exposes the unkeyed default
BREAKING Enhancement Blazor WebAssembly
BaseKeyValueStore and SessionStorageKeyValueStore have been removed. The Web package now requires the _content/Shiny.Extensions.Stores.Web/shiny-storage.js script to be loaded in index.html before blazor.webassembly.js
BREAKING Enhancement
FileKeyValueStore has been removed — use the new IRepository API for entity-style persistence
Fix Android
SecureKeyValueStore is now functional — its constructor was previously commented out and the class could not be instantiated
Fix Windows
SecureKeyValueStore had a syntactically broken constructor; the class is now functional
Enhancement
Updated to align with Shiny Extensions 2.0 — no API changes
Fix
Booleans with false were not binding properly on stores
Fix iOS
Secure storage would not correctly store or clear all values
Feature
Initial Public Release