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
آخر نشاط في المصدر
١٥ أغسطس ٢٠٢٦ في ١٧:١٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٤
التفرعات
٠

خيارات التثبيت

يُحدَّد 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