Lyo.Config 1.0.1

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

Lyo.Config

Typed, definition-driven configuration for per-entity values (e.g. a Discord guild, a tenant). The abstract API lives here; PostgreSQL persistence is in Lyo.Config.Postgres.

Concepts

Piece Role
ConfigDefinitionRecord Declares an allowed key for an ForEntityType (CLR type name string), the CLR value type, optional default, IsRequired, and metadata.
ConfigBindingRecord Stores the actual value for one entity instance (EntityRef: type + id) under a definition.
ConfigValue Wrapper: CLR type name + JSON payload. Serialize/deserialize with ConfigJsonSerializerOptions.Default when you do not pass custom JsonSerializerOptions.
ResolvedConfigRecord Produced by LoadConfigAsync: every definition for that entity type, each with optional binding; Value is binding ?? default.

Definitions are unique per (ForEntityType, Key). Bindings are unique per (DefinitionId, ForEntityType, ForEntityId). In PostgreSQL, config_binding has a value_type column ( same CLR type name as config_definition.for_value_type, denormalized for querying and exports).

JSON

ConfigJsonSerializerOptions.Default is used whenever ConfigValue callers pass null for options: camelCase property names, case-insensitive deserialization, omit nulls when writing. Keeps API JSON and stored value_json aligned.

IsRequired

If IsRequired is true and the definition has no default (DefaultValue null), each entity must have a binding for that key. - LoadConfigAsync calls ResolvedConfigRecord.ValidateRequired() and throws if any required key has no resolved value. - DeleteBindingAsync / DeleteBindingsAsync refuse to remove a binding that would violate that rule. If IsRequired is true and a default exists, the default supplies the resolved value when no binding exists (deleting the binding is allowed).

Deleting definitions

DeleteDefinitionAsync removes the definition row. In PostgreSQL, config_binding rows referencing that definition are removed by foreign-key ON DELETE CASCADE.

Versioning

There are two different “version” stories:

Versioning — 1. CLR / definition type changes

Each definition’s ForValueType is the CLR type name for the stored JSON, same convention as ForEntityType: Type.FullName (same form as ConfigValue.TypeName; use ConfigValue.GetTypeName(typeof(T)) when seeding). If you rename types, split types, or change the JSON shape incompatibly: - Update the definition (and seeders) so ForValueType matches the new type. - Migrate existing value_json (or delete bindings and recreate), or introduce a new key and deprecate the old one. The Lyo.Config layer does not auto-migrate arbitrary payloads.

Versioning — 2. Document schema version inside the JSON (optional pattern)

For a single JSON document stored as one binding (e.g. DiscordGuildSettings), use an integer Version field and a CurrentSchemaVersion constant on the model:

  • NormalizeForRead() — After GetValue<T>(), fix legacy documents (e.g. Version <= 0 or older version numbers): set defaults for new properties, rewrite fields, then set * Version* to the version you’ve upgraded to.
  • NormalizeForPersistence() — Before ConfigValue.From, call this so every save writes Version == CurrentSchemaVersion.

When you add a breaking or additive shape change: bump CurrentSchemaVersion, extend NormalizeForRead() with if (Version == n) { …; Version = n + 1; } (or jump straight to current), and deploy readers before or with writers.

This is application-level migration inside one binding value; it does not replace backups or one-off SQL migrations when you need them.

Versioning — 3. Binding value history (revert)

PostgreSQL stores append-only revisions in config.config_binding_revision: primary key is (binding_id, revision) (no separate row id). Each successful * SaveBindingAsync* writes a new row with a monotonic revision number (1-based per binding). The current value still lives on config_binding for fast reads.

  • GetBindingRevisionsAsync / GetBindingRevisionAsync — inspect history (newest first in the list overload).
  • RevertBindingToRevisionAsync — copies the snapshot at revision onto the binding and appends a new revision (so the timeline stays linear and “revert” is auditable).

Deleting a binding (or its definition) cascades and removes revision rows. The initial migration seeds revision 1 from each existing binding’s current value so history starts at deploy time.

IConfigStore at a glance

  • SaveDefinitionAsync(ConfigDefinitionRecord) — upsert.
  • GetDefinitionByIdAsync(Guid) / GetDefinitionAsync(string forEntityType, string key) — single lookups.
  • GetDefinitionsAsync(string forEntityType) — enumerate definitions for a type.
  • DeleteDefinitionAsync(Guid) — Postgres cascades to config_binding rows.

API routing helper — AppConfigEntity

  • AppEntityType = "App" (the stored EntityRef.EntityType for app-scoped definitions and bindings).
  • ToEntityRef(string appKind, string appId) / TryCreate(...) — URI-decode each segment, lowercase, validate the slug character set (a-z, 0-9, -, _, ., length ≤ 128), and produce new EntityRef("App", $"{kindNorm}:{idNorm}"). This is the compound-id shape that ConfigBindingRecord.ForEntityId is sized for (string, not Guid).

See also

Dependencies

Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).

  • Lyo.Common — (direct, lyo)
  • Lyo.EntityReference.Models — (direct, lyo)
  • System.Text.Json 10.0.5 — (direct, microsoft, netstandard2.0)
  • Lyo.Exceptions — (transitive, lyo)
  • Microsoft.Extensions.Logging.Abstractions 10.0.5 — (transitive, microsoft)
  • System.Memory 4.6.3 — (transitive, microsoft, netstandard2.0)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Lyo.Config:

Package Downloads
Lyo.Config.Api.Models

Shared contracts for the Lyo central Config API (resolve outcomes).

Lyo.Config.Postgres

PostgreSQL implementation of Lyo.Config using Entity Framework Core and jsonb-backed typed values.

Lyo.Discord.Postgres

PostgreSQL persistence for Discord entities (EF Core, schema discord).

Lyo.Config.Api

Central HTTP API exposing Lyo.Config PostgreSQL-backed IConfigStore for microservices (polling-friendly ETags). Embed via AddConfigApi / MapConfigApiEndpoints.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 0 8/18/2026
1.0.0 125 8/16/2026