Shiny.Extensions.Stores 4.0.0-beta-0059
Shiny Extensions
Dependency Injection Extensions
- Source generate all attributed classes to a single add file - saves you the boilerplate
- Factory-form emission - generated registrations expand the constructor at compile time (no reflection, AOT-clean) so resolve chains like
OnResolvedcompose naturally ActivatorUtilities-style constructor selection ([ActivatorUtilitiesConstructor]and[FromKeyedServices]honored)- Multiple interfaces via explicit forwarders (no reflection)
- Supports open generics and keyed services
OnResolved<T>(hook)chain extension for one-shot post-construction hooks
The Results
THIS:
using Microsoft.Extensions.DependencyInjection;
using Shiny;
// given the following code from a user
namespace Sample
{
public interface IStandardInterface;
public interface IStandardInterface2;
[Service(ServiceLifetime.Singleton)]
public class ImplementationOnly;
[Service(ServiceLifetime.Transient, "ImplOnly")]
public class KeyedImplementationOnly;
[Service(ServiceLifetime.Singleton)]
public class StandardImplementation : IStandardInterface;
[Service(ServiceLifetime.Scoped, "Standard")]
public class KeyedStandardImplementation : IStandardInterface;
[Service(ServiceLifetime.Singleton)]
public class MultipleImplementation : IStandardInterface, IStandardInterface2;
[Service(ServiceLifetime.Scoped)]
public class ScopedMultipleImplementation : IStandardInterface, IStandardInterface2;
[Service(ServiceLifetime.Scoped, "KeyedGeneric")]
public class TestGeneric<T1, T2>
{
public T1 Value1 { get; set; }
public T2 Value2 { get; set; }
}
}
GENERATES THIS:
// <auto-generated />
using global::Microsoft.Extensions.DependencyInjection;
using global::Shiny;
namespace Sample
{
public static class __GeneratedRegistrations
{
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddGeneratedServices(
this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services
)
{
services.AddSingleton<global::Sample.ImplementationOnly>();
services.AddKeyedTransient<global::Sample.KeyedImplementationOnly>("ImplOnly");
services.AddSingleton<global::Sample.IStandardInterface, global::Sample.StandardImplementation>();
services.AddKeyedScoped<global::Sample.IStandardInterface, global::Sample.KeyedStandardImplementation>("Standard");
services.AddSingletonAsImplementedInterfaces<global::Sample.MultipleImplementation>();
services.AddScopedAsImplementedInterfaces<global::Sample.ScopedMultipleImplementation>();
services.AddKeyedScoped(typeof(global::Sample.TestGeneric<,>), "KeyedGeneric");
return services;
}
}
}
Setup
- Install the NuGet package
Shiny.Extensions.DependencyInjection - Add the following using directive:
// during your app startup - use your service collection builder.Services.AddGeneratedServices(); - Add the
[Service(ServiceLifetime.Singleton, "optional key")]attribute to your classes and specify the lifetime and optional key
Stores
- Cross-platform key/value store with support for
- Android/iOS/Windows - Preferences & Secure Storage
- Web - Local Storage
- In Memory
- Source-generated
[Bind]attribute on partial properties - emits getter/setter bodies that round-trip through the store (no INPC required, no runtime reflection, fully AOT) - Static
Shiny.Stores.Default/Secure/Keyed(...)accessor for direct ad-hoc reads/writes - Implement
IKeyValueStoreto plug in your own store
Setup
- Install the NuGet package
Shiny.Extensions.Stores - Register at startup and initialize the static accessor after build:
builder.Services.AddShinyStores(); var host = builder.Build(); host.Services.UseShinyStores(); // wires up the static Shiny.Stores accessor - Define your settings as a
partialclass with[Bind]partial properties: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; } } - Inject
AppSettingsanywhere. Set properties — they persist. Read properties — they come from the store.
Or skip the class and use the static accessor:
Shiny.Stores.Default.Set("theme", "dark");
var theme = Shiny.Stores.Default.Get<string>("theme");
Available Stores Per Platform
| Platform | Key | Description |
|---|---|---|
| Android | StoreKeys.Default |
Preferences |
| Android | StoreKeys.Secure |
Secure Storage |
| iOS | StoreKeys.Default |
NSUserDefaults |
| iOS | StoreKeys.Secure |
Keychain |
| Windows | StoreKeys.Default |
ApplicationData.LocalSettings |
| Windows | StoreKeys.Secure |
Secure Storage |
| WebAssembly | StoreKeys.Default |
localStorage |
| WebAssembly | "session" |
sessionStorage |
| All | any | In-memory dictionary (great for testing) |
[!NOTE] For WebAssembly, install the
Shiny.Extensions.Stores.Webpackage and addservices.AddShinyWebAssemblyStores()to your service collection.
Web Hosting Extensions
- Merges service container build and post build scenarios into a single class using
IWebModule
Setup
- Install the NuGet package
Shiny.Extensions.WebHosting - Add a web module by implementing
IWebModule:using Shiny; public class MyWebModule : IWebModule { public void Add(WebApplicationBuilder builder) { // Register your services here } public void Use(WebApplication app) { // Configure your middleware/endpoints here } } - In your application hosting startup, add the following:
using Shiny; var builder = WebApplication.CreateBuilder(args); builder.AddInfrastructureModules(new MyWebModule()); var app = builder.Build(); app.UseInfrastructureModules();
MAUI Hosting Extensions
- Module-based MAUI app configuration with
IMauiModule - Static
Host.Servicesfor accessing the service provider anywhere IAppSupport— device info, browser/map launch, programmatic orientation lock, and live change events for orientation, culture, and time zone (native listeners on iOS/Android/Windows, polling fallback elsewhere)IAppStore— cross-platform store version lookups + deep links for Apple App Store (iTunes Search API), Google Play (HTML scrape), and Microsoft Store (DisplayCatalog API)- Opt-in registration: each capability is its own extension method so apps only pay for what they use
Setup
- Install the NuGet package
Shiny.Extensions.MauiHosting - Create a MAUI module by implementing
IMauiModule:using Shiny; public class MyMauiModule : IMauiModule { public void Add(MauiAppBuilder builder) { // Register your services here } public void Use(IPlatformApplication app) { // Post-build initialization here (do NOT block) } } - In your
MauiProgram.cs:using Shiny; var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .AddInfrastructureModules(new MyMauiModule()) // your IMauiModule list .AddAppSupport() // IAppSupport .AddAppStore(opts => // optional: IAppStore + config { opts.AppleAppId = "1234567890"; opts.WindowsProductId = "9NBLGGH4NNS1"; }); return builder.Build(); - Access services anywhere via
Host.Services
IAppSupport
public class MyVm(IAppSupport app)
{
void Hook()
{
// Snapshot
var version = app.AppVersion;
var orientation = app.CurrentOrientation;
var culture = app.CurrentCulture;
// Live updates
app.OrientationChanged += (s, e) => { /* new DisplayOrientation */ };
app.CultureChanged += (s, e) => { /* new CultureInfo */ };
app.TimeZoneChanged += (s, e) => { /* new TimeZoneInfo */ };
// Programmatic orientation lock
_ = app.SetOrientation(DisplayOrientation.Landscape);
_ = app.ResetOrientation();
}
}
IAppStore
public class UpdateChecker(IAppStore store)
{
public async Task Check()
{
var result = await store.GetCurrent();
if (result?.NeedsUpdate == true)
await store.OpenStore();
}
public Task Review() => store.OpenReviewPage();
}
Additional Libraries Used
- Shiny Reflector - Reflection without the actual reflection
NuGet Packages
| Package | Description |
|---|---|
Shiny.Extensions.DependencyInjection |
Attribute-driven DI registration with source generators |
Shiny.Extensions.Stores |
Cross-platform key/value store abstraction |
Shiny.Extensions.Stores.Web |
Blazor WebAssembly localStorage/sessionStorage |
Shiny.Extensions.WebHosting |
ASP.NET modular web hosting with IWebModule |
Shiny.Extensions.MauiHosting |
MAUI modular hosting with IMauiModule and platform lifecycle hooks |
Showing the top 20 packages that depend on Shiny.Extensions.Stores.
| Packages | Downloads |
|---|---|
|
Shiny.Extensions.DependencyInjection
Utilities for Microsoft.Extensions.DependencyInjection as well as source generation to remove some boilerplating
|
2 |
|
Shiny.Hosting.Maui
Shiny
|
1 |
|
Shiny.Jobs
Shiny Background Jobs
|
1 |
|
Shiny.Core
The Shiny Core Foundation where all Shiny modules are built on
|
1 |
.NET 10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
.NET 10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
.NET 10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
.NET 10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
| Version | Downloads | Last updated |
|---|---|---|
| 5.1.1 | 0 | 06/20/2026 |
| 5.1.0 | 0 | 06/14/2026 |
| 5.1.0-beta-0054 | 0 | 06/14/2026 |
| 5.1.0-beta-0052 | 1 | 06/13/2026 |
| 5.1.0-beta-0051 | 1 | 06/13/2026 |
| 5.0.0 | 1 | 06/13/2026 |
| 5.0.0-beta-0052 | 1 | 06/13/2026 |
| 5.0.0-beta-0051 | 1 | 06/13/2026 |
| 4.1.1 | 1 | 06/13/2026 |
| 4.1.0 | 1 | 06/13/2026 |
| 4.0.0 | 1 | 06/13/2026 |
| 4.0.0-beta-0063 | 1 | 06/13/2026 |
| 4.0.0-beta-0062 | 1 | 06/13/2026 |
| 4.0.0-beta-0061 | 1 | 06/13/2026 |
| 4.0.0-beta-0060 | 1 | 06/13/2026 |
| 4.0.0-beta-0059 | 1 | 06/13/2026 |
| 4.0.0-beta-0058 | 1 | 06/13/2026 |
| 4.0.0-beta-0057 | 1 | 06/13/2026 |
| 4.0.0-beta-0056 | 1 | 06/13/2026 |
| 4.0.0-beta-0041 | 1 | 06/13/2026 |
| 4.0.0-beta-0005 | 1 | 06/13/2026 |
| 4.0.0-beta-0004 | 1 | 06/13/2026 |
| 4.0.0-beta-0003 | 1 | 06/13/2026 |
| 4.0.0-beta-0002 | 1 | 06/13/2026 |
| 3.0.0 | 1 | 06/13/2026 |
| 2.1.0-beta-0045 | 1 | 06/13/2026 |
| 2.1.0-beta-0044 | 1 | 06/13/2026 |
| 2.1.0-beta-0043 | 1 | 06/13/2026 |
| 2.1.0-beta-0042 | 1 | 06/13/2026 |
| 2.1.0-beta-0041 | 1 | 06/13/2026 |
| 2.0.4 | 1 | 06/13/2026 |
| 2.0.3 | 1 | 06/13/2026 |
| 2.0.2 | 1 | 06/13/2026 |
| 2.0.1 | 1 | 06/13/2026 |
| 2.0.0 | 1 | 06/13/2026 |
| 2.0.0-beta-0047 | 1 | 06/13/2026 |
| 2.0.0-beta-0006 | 1 | 06/13/2026 |
| 2.0.0-beta-0005 | 1 | 06/13/2026 |
| 2.0.0-beta-0001 | 1 | 06/13/2026 |
| 1.3.1 | 1 | 06/13/2026 |
| 1.3.1-beta-0001 | 1 | 06/13/2026 |
| 1.3.0 | 1 | 06/13/2026 |
| 1.3.0-beta-0009 | 1 | 06/13/2026 |
| 1.3.0-beta-0008 | 1 | 06/13/2026 |
| 1.3.0-beta-0007 | 1 | 06/13/2026 |
| 1.3.0-beta-0006 | 1 | 06/13/2026 |
| 1.3.0-beta-0004 | 1 | 06/13/2026 |
| 1.3.0-beta-0003 | 1 | 06/13/2026 |
| 1.3.0-beta-0002 | 1 | 06/13/2026 |
| 1.3.0-beta-0001 | 1 | 06/13/2026 |
| 1.2.2 | 1 | 06/13/2026 |
| 1.2.1 | 1 | 06/13/2026 |
| 1.2.0 | 1 | 06/13/2026 |
| 1.2.0-beta-0001 | 1 | 06/13/2026 |
| 1.1.0 | 1 | 06/13/2026 |
| 1.1.0-beta-0009 | 1 | 06/13/2026 |
| 1.1.0-beta-0008 | 1 | 06/13/2026 |
| 1.1.0-beta-0007 | 1 | 06/13/2026 |
| 1.1.0-beta-0006 | 1 | 06/13/2026 |
| 1.1.0-beta-0004 | 1 | 06/13/2026 |
| 1.1.0-beta-0003 | 1 | 06/13/2026 |
| 1.1.0-beta-0002 | 1 | 06/13/2026 |
| 1.0.3 | 1 | 06/13/2026 |
| 1.0.2 | 1 | 06/13/2026 |
| 1.0.1 | 1 | 06/13/2026 |
| 1.0.0 | 1 | 06/13/2026 |
| 1.0.0-g5fce6b880d | 1 | 06/13/2026 |
| 1.0.0-alpha-0020 | 1 | 06/13/2026 |
| 1.0.0-alpha-0019 | 1 | 06/13/2026 |
| 1.0.0-alpha-0018 | 1 | 06/13/2026 |
| 1.0.0-alpha-0017 | 1 | 06/13/2026 |
| 1.0.0-alpha-0016 | 1 | 06/13/2026 |
| 1.0.0-alpha-0015 | 1 | 06/13/2026 |
| 1.0.0-alpha-0014 | 1 | 06/13/2026 |
| 1.0.0-alpha-0013 | 1 | 06/13/2026 |
| 1.0.0-alpha-0011 | 1 | 06/13/2026 |
| 1.0.0-alpha-0010 | 1 | 06/13/2026 |
| 1.0.0-alpha-0009 | 1 | 06/13/2026 |