Skip to main content

shiny-stores

Generate and configure Shiny Stores for .NET - cross-platform key/value stores with source-generated property binding for mobile, desktop, and Blazor WebAssembly

跳到安装

来源信息

仓库
shinyorg/skills
最近来源活动
2026年8月15日 17:13
检测到的 SKILL.md 语言
英语
星标
4
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
shiny-stores
description
Generate and configure Shiny Stores for .NET - cross-platform key/value stores with source-generated property binding for mobile, desktop, and Blazor WebAssembly
auto_invoke
true
triggers
["IKeyValueStore","AddShinyStores","AddShinyWebAssemblyStores","StoreKeys","Shiny.Stores","BindAttribute","Shiny.Extensions.Stores","Shiny.Extensions.Stores.Web"]
# Shiny Stores Skill You are an expert in Shiny Extensions Stores, a .NET library providing cross-platform key/value store abstraction with source-generated property binding. ## When to Use This Skill Invoke this skill when the user wants to: - Use cross-platform key/value stores (settings, secure storage, memory) - Persist properties to a backing store using the `[Bind]` source generator - Use key/value stores in Blazor WebAssembly (localStorage) - Create custom key/value store implementations ## Library Overview **Documentation**: https://shinylib.net/extensions/stores/ **Repository**: https://github.com/shinyorg/Shiny.Extensions **Packages**: `Shiny.Extensions.Stores`, `Shiny.Extensions.Stores.Web` ## Built-in Store Keys Stores are registered as **keyed** singletons in DI using `StoreKeys` constants: | Key | Platform | Implementation | |-----|----------|---------------| | `StoreKeys.Default` ("settings") | Android | SharedPreferences | | `StoreKeys.Default` ("settings") | iOS / Mac Catalyst / macOS (`net10.0-macos`) | NSUserDefaults | | `StoreKeys.Default` ("settings") | Windows (packaged) | ApplicationData.LocalSettings | | `StoreKeys.Default` ("settings") | Linux / other desktop / unpackaged Windows | `FileKeyValueStore` (JSON file) | | `StoreKeys.Default` ("settings") | Blazor | localStorage | | `StoreKeys.Secure` ("secure") | Android | EncryptedSharedPreferences | | `StoreKeys.Secure` ("secure") | iOS / Mac Catalyst / macOS (`net10.0-macos`) | Keychain | | `StoreKeys.Secure` ("secure") | Windows | DPAPI (packaged: ApplicationData; unpackaged: over file) | | `StoreKeys.Secure` ("secure") | Linux / other desktop | `FileKeyValueStore` (JSON file, **not encrypted**) | `net10.0-macos` (plain macOS desktop apps) uses Foundation NSUserDefaults + Security.framework Keychain — the same code as iOS/Mac Catalyst — so it gets **real** secure storage, not the plaintext file fallback. Any target that resolves the base `net10.0` asset (Linux, unpackaged Windows, other desktop) uses `FileKeyValueStore`, which persists to `{LocalApplicationData}/{EntryAssemblyName}` — so settings survive restarts instead of living only in memory. Override the folder via `Shiny.Stores.FileStoreDirectory` **before first access**. On these file fallbacks `Secure` is a plain JSON file and is **not encrypted** (unpackaged Windows keeps DPAPI over the file); do not put genuinely sensitive secrets there. ## Setup ```csharp // Mobile / desktop — platform-native stores services.AddShinyStores(); // Blazor WebAssembly — localStorage (still needs UseShinyStores after Build, // because IJSRuntime is only available post-build) services.AddShinyWebAssemblyStores(); ``` On mobile/desktop you **do not need** a post-build `UseShinyStores()` call. `Shiny.Stores.Default` / `Shiny.Stores.Secure` are self-bootstrapping: on first access they lazily create the platform-native store (SharedPreferences / NSUserDefaults / Keychain / DPAPI) or a persistent `FileKeyValueStore` on desktop targets that resolve the base `net10.0` asset. `AddShinyStores()` just registers those same instances into DI so keyed `IKeyValueStore` injections share them. For Blazor (where the store needs `IJSRuntime` from the built provider) call `host.Services.UseShinyStores()` after `host.Build()` to snapshot the keyed `IKeyValueStore` registrations into the static accessor. ## Static `Shiny.Stores` Accessor The simplest way to read/write — self-bootstraps on first access. No initialization required for mobile/desktop. ```csharp Shiny.Stores.Default.Set("theme", "dark"); var theme = Shiny.Stores.Default.Get<string>("theme"); Shiny.Stores.Secure.Set("token", "abc123"); // Arbitrary keyed stores (must be registered with Stores.Register or via DI + UseShinyStores) Shiny.Stores.Keyed("my-store").Set("k", "v"); ``` ### Overrides / Tests / Custom Keys ```csharp // Swap any key for a test double or custom backend Shiny.Stores.Register(StoreKeys.Default, new MemoryKeyValueStore()); Shiny.Stores.Register("redis", new RedisKeyValueStore(...)); // Or snapshot keyed IKeyValueStore registrations from a built provider: serviceProvider.UseShinyStores(); // Equivalent low-level call: Shiny.Stores.Initialize(serviceProvider); // Reset between tests: Shiny.Stores.Reset(); ``` ## DI-Style Access ```csharp public class SettingsService( [FromKeyedServices(StoreKeys.Default)] IKeyValueStore settings, [FromKeyedServices(StoreKeys.Secure)] IKeyValueStore secure ) { public void SaveTheme(string theme) => settings.Set("theme", theme); public string GetTheme() => settings.Get<string>("theme") ?? "light"; } ``` `AddShinyStores()` also registers the default store **unkeyed**, so a plain `IKeyValueStore` parameter resolves to the same instance as `[FromKeyedServices(StoreKeys.Default)]`. ### Third-party containers Container adapters that predate .NET 8 keyed services — Prism's DryIoc container is the common one, it still sits on DryIoc 5.x — silently **ignore** `[FromKeyedServices]` and resolve the plain service type instead. The unkeyed registration above is what keeps the default store working there. `StoreKeys.Secure` has no such fallback (registering two different stores under one service type would be ambiguous), so when generating code for an app on a non-Microsoft container, reach for the static accessor instead: ```csharp public class SettingsService { readonly IKeyValueStore secure = Shiny.Stores.Secure; // or Shiny.Stores.Keyed("my-store") } ``` A DryIoc `Error.UnableToFindCtorWithAllResolvableArgs` naming a type that injects a keyed store is this problem — DryIoc's `ConstructorWithResolvableArguments` rule reports the outermost type, not the dependency that actually failed. ## Source-Generated `[Bind]` Properties The DI source generator (from `Shiny.Extensions.DependencyInjection`) recognizes `[Bind]` on partial properties and emits getter/setter bodies that round-trip through the static `Shiny.Stores` accessor. ```csharp using Shiny; [Singleton] public partial class AppSettings { [Bind] // default store public partial string Theme { get; set; } [Bind("secure")] // secure store public partial string Token { get; set; } [Bind(Key = "ui_density")] // override storage key public partial int Density { get; set; } } ``` No `INotifyPropertyChanged`, no runtime reflection. Generated property bodies call `Shiny.Stores.Default/Secure/Keyed(...).Get<T>(...)` and `.Set(...)`. ## Store Extension Methods ```csharp store.Get<T>(key, defaultValue); // Get with default store.GetRequired<T>(key); // Throws if not found store.SetOrRemove(key, value); // Removes if value is null store.SetDefault<T>(key, value); // Only sets if key doesn't exist store.IncrementValue(key); // Thread-safe integer increment ``` ## Code Generation Instructions - Use `AddShinyStores()` for mobile/desktop, `AddShinyWebAssemblyStores()` for Blazor - For persistent settings, prefer `[Singleton]` + `[Bind]` partial properties over manual `Set`/`Get` calls - The class with `[Bind]` properties must be `partial`; properties must also be `partial` - For sensitive data (tokens, credentials), pass `"secure"` to `[Bind("secure")]` - Use `Shiny.Stores.Default`/`Secure`/`Keyed(...)` for direct ad-hoc access ## Best Practices 1. **Use `[Bind]` for settings classes** — eliminates boilerplate, no INPC needed, AOT-clean 2. **Target the secure store** — always use `[Bind("secure")]` for sensitive values 3. **Mobile/desktop needs no post-build call** — `Shiny.Stores` self-bootstraps on first access. `UseShinyStores()` is only needed for Blazor (because `IJSRuntime` requires the built provider) or when you've registered custom keyed `IKeyValueStore`s in DI that you want snapshotted into the static accessor 4. **Use `Stores.Register` in tests** — pair with `Stores.Reset()` between tests to swap in `MemoryKeyValueStore` or any custom double
在 GitHub 查看