skipnothing/.NET Platform

Options Validation

A `MaxPoolSize` of 5000 sits in appsettings.json, binds cleanly into a typed object, and crashes nothing at startup. The app runs for hours on the bad value, then throws on the one request that finally reads it.

  • A MaxPoolSize of 5000 binds without complaint; the rule that rejects it only runs if you register one.
  • Rules run lazily by default: bad config boots green and fails on the first request that reads it.
  • Same rule, two failure moments: on the first read, or at startup, one line apart.
BUILDS ON
01

Binding accepts what validation rejects

MaxPoolSize sits at 5000 in appsettings.json. It is an integer, the property it binds to is an int, so binding succeeds: the typed object comes back with MaxPoolSize == 5000 and IOptions<DatabaseOptions>.Value hands it over without a word.

Binding only asks one question: can this string become that type? 5000 parses to an int, so the answer is yes. Whether 5000 is a *sane* pool size, whether it sits under some ceiling, whether it agrees with the other fields, binding never asks. Those are questions about meaning, and binding checks shape.

So the object is structurally valid and semantically wrong at the same time, and nothing in the pipeline notices. The app starts. Requests flow. The connection pool tries to open 5000 connections. The failure, when it comes, points at the database driver, not at the config, three layers away from the line that was actually wrong.

That gap, between a value that binds and a value that is correct, is the whole reason validation exists. The rest of this topic fills it: how to state the rule, when to run it, what the failure looks like, and how to let the compiler write the check.

BIND SUCCEEDS, MEANING GOES UNCHECKED

Keep going, sign up to unlock the rest

4 more parts in this topic, plus 25+ more topics in .NET Platform.

Sign up, it's freeSee the full .NET Platform
Configuration & Options0/5#19 The Options Pattern
#21 Environments & Feature Flags