Settify.Caching 1.3.0

dotnet add package Settify.Caching --version 1.3.0
                    
NuGet\Install-Package Settify.Caching -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Settify.Caching" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Settify.Caching" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Settify.Caching" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Settify.Caching --version 1.3.0
                    
#r "nuget: Settify.Caching, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Settify.Caching@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Settify.Caching&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Settify.Caching&version=1.3.0
                    
Install as a Cake Tool

Settify

Database-backed, strongly-typed settings with entity-scoped cascade resolution for .NET.

Features

  • Strongly-typed settings — define settings as plain C# classes
  • Typed DI injectionISettings<T> with scope metadata from [Settings] attribute
  • Source generator — compile-time validation (SETKIT001-003), AOT-friendly auto-registration
  • Cascade resolution — Entity → Global → Default fallback chain
  • Object-level migrations — versioned schema evolution applied on read
  • Multiple providers — In-Memory, Dapper, EF Core
  • Caching — IMemoryCache and HybridCache decorators with tag-based invalidation
  • Validation — DataAnnotations support with custom validators
  • Encryption[Secret] attribute with pluggable encryption for sensitive properties
  • Auto-detect SQL dialect — Dapper provider auto-detects from IDbConnection type

Quick Start

// Define your settings with the [Settings] attribute
[Settings(Scope = SettingsScope.Global)]
public class AppSettings : ISettings
{
    public string SiteName { get; set; } = "My App";
    public int MaxUploadSize { get; set; } = 10485760;
}

[Settings(Scope = SettingsScope.Entity, EntityType = "Project")]
public class ProjectSettings : ISettings
{
    public string TicketPrefix { get; set; } = "PROJ";
    public bool EnableDigest { get; set; }
}

// Register in DI — settings types are auto-discovered by the source generator
builder.Services.AddSettify(opts => opts.ValidateOnSave = true)
    .UseInMemory();

// Inject typed settings
app.MapGet("/settings", async (ISettings<AppSettings> settings) =>
    await settings.GetAsync());

app.MapGet("/project/{id}/settings", async (string id, ISettings<ProjectSettings> settings) =>
    await settings.GetAsync(id));

app.MapPut("/project/{id}/settings", async (string id, ISettings<ProjectSettings> settings) =>
{
    await settings.UpdateAsync(s => s.TicketPrefix = "ACME", id);
    return Results.Ok();
});

The ISettingsManager API is still fully supported for cases where you need direct control:

app.MapGet("/settings", async (ISettingsManager manager) =>
    await manager.GetGlobalAsync<AppSettings>());

[Settings] Attribute

The [Settings] attribute provides scope metadata and enables compile-time validation via the built-in source generator.

// Global settings — one instance per type
[Settings(Scope = SettingsScope.Global)]
public class AppSettings : ISettings { ... }

// Entity-scoped settings — one instance per entity
[Settings(Scope = SettingsScope.Entity, EntityType = "Project")]
public class ProjectSettings : ISettings { ... }

// Custom group key override
[Settings(Scope = SettingsScope.Global, GroupKey = "app-config")]
public class AppConfig : ISettings { ... }

Compile-Time Diagnostics

ID Severity Rule
SETKIT001 Error Entity-scoped settings must specify EntityType
SETKIT002 Error Settings class must have a public parameterless constructor
SETKIT003 Error [Settings] class must implement ISettings

ISettings<T> — Typed Injection

ISettings<T> is automatically registered for each [Settings]-annotated class. It resolves scope and entity type from the attribute.

public interface ISettings<T> where T : class, ISettings, new()
{
    Task<T?> GetAsync(string? entityId = null, CancellationToken ct = default);
    Task SaveAsync(T settings, string? entityId = null, CancellationToken ct = default);
    Task<bool> DeleteAsync(string? entityId = null, CancellationToken ct = default);
    Task<T> ResolveMergedAsync(string entityId, CancellationToken ct = default);
    Task UpdateAsync(Action<T> mutator, string? entityId = null, CancellationToken ct = default);
}
  • Global scope: GetAsync() — no entityId needed
  • Entity scope: GetAsync("123")entityId is required (throws ArgumentException if null)
  • UpdateAsync: reads current value, applies your mutator, saves back

Migrations

Settify supports object-level migrations for schema evolution. Migrations run automatically on read when a stored version is behind the latest.

public class AddDigestSetting : SettingsMigration<ProjectSettings>
{
    public override int Version => 2;
    public override void Up(ProjectSettings settings)
    {
        settings.EnableDigest = false;
    }
}

Register migrations at startup:

builder.Services.AddSettify(opts =>
{
    opts.AddMigrationsFromAssembly(typeof(Program).Assembly);
})

Or register individually:

opts.AddMigration<AddDigestSetting>();

How It Works

  1. Provider reads (json, version) from storage
  2. Manager deserializes → runs pending Up() methods in version order
  3. Manager serializes back and saves with the new version
  4. Version is stored as an INT DEFAULT 0 column — not inside the JSON

Providers that don't implement IVersionedGlobalSettingsProvider / IVersionedEntitySettingsProvider will skip migrations automatically (backward compatible).

Providers

In-Memory

builder.Services.AddSettify().UseInMemory();

Dapper

builder.Services.AddSettify()
    .UseDapper(cfg =>
    {
        cfg.AutoCreateTables = true;
        cfg.Global.TableName = "global_settings";
        cfg.Entity.Strategy = EntityTableStrategy.SharedTable;
    });

SQL dialect is auto-detected from IDbConnection type (SQL Server, PostgreSQL, SQLite).

EF Core

builder.Services.AddSettify()
    .UseEntityFrameworkCore<AppDbContext>();

In your DbContext:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplySettifyConfiguration();
}

Caching

IMemoryCache

builder.Services.AddSettify()
    .UseDapper(cfg => { ... })
    .UseMemoryCache(opts => opts.Expiration = TimeSpan.FromMinutes(10));

HybridCache

builder.Services.AddSettify()
    .UseDapper(cfg => { ... })
    .UseHybridCache(opts =>
    {
        opts.Expiration = TimeSpan.FromMinutes(5);
        opts.Tag = "settingskit";
    });

HybridCache supports L1 (in-process) + L2 (distributed) caching. Tag-based invalidation is used on save/delete — entries are tagged with group:{groupKey} and entity:{entityType}:{entityId} for bulk eviction via RemoveByTagAsync.

Encryption

Mark sensitive properties with [Secret] to automatically encrypt them at rest. Non-secret properties remain as plaintext in the stored JSON.

[Settings(Scope = SettingsScope.Global)]
public class IntegrationSettings : ISettings
{
    public string ServiceUrl { get; set; } = "";

    [Secret]
    public string ApiKey { get; set; } = "";

    [Secret]
    public string WebhookSecret { get; set; } = "";
}

Quick Setup

The simplest option — uses AES-256-GCM with an auto-generated key stored at ~/.settingskit/encryption.key:

builder.Services.AddSettify()
    .UseEncryption()
    .UseDapper(cfg => { ... });

Custom Key File Path

builder.Services.AddSettify()
    .UseEncryption("/etc/myapp/settings.key");

Explicit Key

builder.Services.AddSettify()
    .UseEncryption(new AesSettingsEncryptor("base64-encoded-32-byte-key"));

Custom Encryptor

Implement ISettingsEncryptor to use DPAPI, Azure Key Vault, AWS KMS, or any other provider:

public class KeyVaultEncryptor : ISettingsEncryptor
{
    public byte[] Encrypt(byte[] plaintext) { ... }
    public byte[] Decrypt(byte[] ciphertext) { ... }
}

builder.Services.AddSettify()
    .UseEncryption<KeyVaultEncryptor>();

How It Works

  • On save, [Secret] property values are encrypted and stored as $enc$<base64> strings in the JSON
  • On read, $enc$-prefixed values are decrypted back to their original types
  • Non-secret properties are unaffected
  • Existing plaintext data is read as-is (backward compatible) and encrypted on the next save
  • Cascade merging, migrations, and validation all work transparently with encrypted properties
  • If no encryptor is registered, [Secret] is ignored and values are stored as plaintext

Stored JSON Example

{
  "serviceUrl": "https://api.example.com",
  "apiKey": "$enc$SGVsbG8gV29ybGQ...",
  "webhookSecret": "$enc$dGVzdCBkYXRh..."
}

Packages

Package Description
Settify Core interfaces, manager, in-memory providers, source generator
Settify.Dapper Dapper global + entity providers
Settify.EntityFrameworkCore EF Core global + entity providers
Settify.Caching IMemoryCache and HybridCache decorators

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 111 4/6/2026
1.2.1 103 4/6/2026
1.2.0 108 3/22/2026
1.0.1 108 3/7/2026