Kalicz.StrongTypes.Configuration 2.0.1

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

Kalicz.StrongTypes.Configuration

NuGet version Downloads License

Stops an options class binding a non-nullable property to null.

builder.Services.AddOptions<RetryOptions>()
    .BindStrongTypes(builder.Configuration.GetSection("Retry"))
    .ValidateOnStart();

The problem

Kalicz.StrongTypes needs no package to bind: every scalar wrapper carries a TypeConverter, so values bind and invalid ones throw with the invariant's own message. This package is only about the key that isn't there.

A wrapper's invariant constrains every value it can hold. It cannot make the binder assign one — the binder reaches in through reflection and never calls Create — and for an absent key it assigns nothing at all. So:

public sealed class RetryOptions
{
    public NonEmptyString Name { get; set; } = null!;   // unconfigured -> null
}

null — which the declaration says is impossible. ValidateOnStart() does not help: binding an absent key succeeds, it just doesn't assign, so nothing is raised. C#'s required doesn't either — it's a compile-time rule and the binder's reflection walks past it. [Required] does work, but only if you remember it on every property.

The analyzer that ships with Kalicz.StrongTypes (ST0004) flags a plain Bind(...) that would leave a non-nullable wrapper null, with a code fix that rewrites the call to BindStrongTypes(...).

What it does

Fails when a property declared non-nullable is null after binding. Your nullable reference annotations already say which properties those are, so nothing has to repeat it:

public NonEmptyString Name { get; set; } = null!;          // fails unless configured
public NonEmptyString? Nickname { get; set; }              // nullable — fine
public string ApiKey { get; set; } = null!;                // fails unless configured
public string Endpoint { get; set; } = "https://x.test";   // has a default — never null
OptionsValidationException: 'Retry:Name' is null. Configure it, give RetryOptions.Name a default,
                            or declare it nullable.

Every reference property is covered, not only the wrappers — a string declared non-nullable is as broken by a missing key as a NonEmptyString is. A declared default needs no configuration and no annotation: it simply isn't null.

At every depth, too: nested options classes, collection elements and dictionary values are walked, and the failure names the full path ('Retry:Endpoints:1:Host'). The walk stops where the binder stops — at any type it has a TypeConverter for — so a wrapper is a leaf, not a graph to descend into.

Pair it with ValidateOnStart(). On its own the failure is still lazy — it surfaces on the first read of IOptions<T>.Value, not at startup.

Value types are not checked, and don't need to be

An unconfigured Positive<int> is 1; an unconfigured bool is false. Those are defaults, and they are values those types are perfectly happy to hold — there is no contradiction to find. Only null in something that promised never to be null is one, and only a reference can manage it.

So this will not tell you that you forgot to configure MaxRetries. If "not configured" has to be distinguishable from a configured default, declare it Positive<int>? and check for null yourself — that is the only way the CLR gives you to say it.

When you don't need it

  • Every reference property on your options classes is nullable or has a default.
  • You already use [Required] with ValidateDataAnnotations(), which covers the same ground with an attribute per property.

Why a separate package

So Kalicz.StrongTypes stays free of dependencies. This package pulls in Microsoft.Extensions.Options.ConfigurationExtensions; a domain model or a library that uses NonEmptyString should not inherit the options and configuration stack transitively just to hold a value. Apps that bind configuration already have it, and add one reference; everyone else keeps a dependency-free core.

Nullable reference types

Intent is read from the assembly's nullable annotations. An options class in a project with <Nullable>disable</Nullable> carries none, so nothing on it is enforced — with no intent declared there is nothing to enforce.

Product Compatible and additional computed target framework versions.
.NET 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
2.0.1 39 7/17/2026
2.0.0 44 7/16/2026