Strongly-typed application settings for .NET. Declare a plain public interface, decorate it with defaults, and SimpleSettings binds your configuration into a runtime implementation you can inject anywhere — no concrete option classes, no per-type services.Configure<> wiring.
dotnet add package ExistForAll.SimpleSettings
dotnet add package ExistForAll.SimpleSettings.Binders
dotnet add package ExistForAll.SimpleSettings.Extensions.GenericHostExistForAll.SimpleSettings— the core binding engine and the directSettingsBuilderAPI.ExistForAll.SimpleSettings.Binders— additional binders (in-memory, command-line, and more).ExistForAll.SimpleSettings.Extensions.GenericHost— dependency-injection integration (AddSimpleSettings).
- Getting started
- Building the collection
- Building config interfaces
- Default Values
- Build section binders
- Extending SimpleSettings
- Security & Behavior
.NET ships IOptions<>, but it couples your application to a framework abstraction, forces every settings shape to be a concrete class rather than an interface, and requires a manual services.Configure<> call per type. SimpleSettings keeps the positioning of IOptions<> — configuration bound into typed objects — while letting you depend on a plain interface, discovered and bound automatically, and portable across DI containers. You inject the interface; SimpleSettings supplies the implementation.
Declare a settings interface and bind it with the direct API:
[SettingsSection]
public interface IEmailSenderSettings
{
[SettingsProperty(DefaultValue = "https://smtp.example.com")]
string ServiceUrl { get; set; }
[SettingsProperty(DefaultValue = 3)]
int Retries { get; set; }
}
var settings = SettingsBuilder.CreateBuilder()
.GetSettings<IEmailSenderSettings>();Every settings interface must be public — SimpleSettings emits a runtime implementation of the interface and cannot implement a non-public one.
- Bind configuration into
publicsettings interfaces — no concrete option classes. - Discover settings via
[SettingsSection], theISettingsSectionmarker base, or aSettingsname suffix. - Per-property defaults, key overrides, custom converters, and required-value enforcement via
[SettingsProperty(...)]. - Object-level and per-property validation through
ISettingValidation<T>. - First-class dependency-injection integration for the .NET Generic Host.
- Value-free exceptions on bind and conversion failures — bound values never surface in error messages (see Security & Behavior).
Register SimpleSettings with the Generic Host and let it discover every settings interface in the supplied assemblies:
services.AddSimpleSettings(o =>
{
o.AddAssemblies(new[] { typeof(IEmailSenderSettings).Assembly });
});
// after building the provider — opt-in, deferred DI validation:
serviceProvider.ValidateSimpleSettings();ValidateSimpleSettings() extends IServiceProvider and must be called after BuildServiceProvider(); attribute and ValidatorType validators run inline during binding and need no such call. See Security & Behavior for the full validation model.
Every exception SimpleSettings throws on a bind or conversion failure is value-free: it carries only type and property metadata and never chains an inner exception that saw the bound value, so a secret you bound cannot surface in the error's ToString() chain. This is a structural guarantee, not a convention.
There are two carve-outs the guard does not reach, and you own them: author-supplied ValidationError message text (it is emitted verbatim), and DI-resolved validator constructors (they run outside the value-free bind guard). Never echo a bound value into a validation message and never log a secret in a validator constructor. See Security & Behavior for the full treatment.
The v1 -> v2 release batched four breaking changes:
SettingsHolder/ISettingsHolderare now internal — use the publicSettingsBuilder/ISettingsCollection/ISettingsProvidersurface instead.- The
Core.AspNetpackage was dropped — it exposed no public type; remove any reference to it. - The
Microsoft.Extensions.*dependency floor is now per-TFM —8.0.xonnet8.0, current onnet10.0. - A public
abstract SimpleSettingsExceptionbase was introduced and boundary exceptions were made public and structured; the old bare-Exceptionthrow for a non-interface settings type is nowSettingsTypeNotInterfaceException.
See the full migration guide in docs/Security.md.