Settify 1.3.0

dotnet add package Settify --version 1.3.0
                    
NuGet\Install-Package Settify -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" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Settify" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Settify" />
                    
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 --version 1.3.0
                    
#r "nuget: Settify, 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@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&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Settify&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 (3)

Showing the top 3 NuGet packages that depend on Settify:

Package Downloads
Settify.EntityFrameworkCore

Entity Framework Core integration for Settify — global and entity-scoped settings providers.

Settify.Caching

Caching decorators for Settify — IMemoryCache and HybridCache wrappers for any settings provider.

Settify.Dapper

Dapper integration for Settify — global and entity-scoped settings providers with auto-detected SQL dialect.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 280 4/6/2026
1.2.1 153 4/6/2026
1.2.0 156 3/22/2026
1.0.1 155 3/7/2026