TimeWarp.OptionsValidation integrates FluentValidation with Microsoft.Extensions.Options to provide automatic validation of your configuration settings at application startup.
Configuration errors are a common source of runtime failures. TimeWarp.OptionsValidation helps you fail fast by validating all configuration settings when your application starts, rather than discovering errors when the configuration is first accessed (which could be hours or days later in production).
Key Benefits:
- Validates configuration settings using FluentValidation rules
- Integrates seamlessly with Microsoft.Extensions.Options
- Catches configuration errors at startup, not at runtime
- Provides clear, actionable error messages
- Supports both IConfiguration binding and programmatic configuration
If you like or are using this project please give it a star. Thank you!
dotnet add package TimeWarp.OptionsValidationYou can see the latest NuGet packages from the official TimeWarp NuGet page.
Use AddFluentValidatedOptions() which returns OptionsBuilder<T>, allowing you to chain with .ValidateOnStart() for automatic startup validation.
using FluentValidation;
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
public int MaxRetries { get; set; }
public int CommandTimeout { get; set; }
// Nested validator - sealed and only used here
public sealed class Validator : AbstractValidator<DatabaseOptions>
{
public Validator()
{
RuleFor(x => x.ConnectionString)
.NotEmpty()
.WithMessage("Database connection string is required");
RuleFor(x => x.MaxRetries)
.GreaterThan(0)
.LessThanOrEqualTo(10)
.WithMessage("MaxRetries must be between 1 and 10");
RuleFor(x => x.CommandTimeout)
.GreaterThanOrEqualTo(30)
.WithMessage("CommandTimeout must be at least 30 seconds");
}
}
}using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register options with automatic startup validation
builder.Services
.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(builder.Configuration)
.ValidateOnStart(); // ✅ Validates when host starts, throws on error
var app = builder.Build();
app.Run(); // Validation happens automatically before this runsWhat this does:
- Binds the
DatabaseOptionssection from appsettings.json - Registers the FluentValidation validator
- Validates configuration at startup (before
app.Run()) - Fails fast with clear error messages if configuration is invalid
- No manual validation calls needed!
The library automatically discovers which configuration section to bind based on simple, predictable rules.
By default, the library uses the class name as the configuration section name:
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
// ...
}Binds to "DatabaseOptions" section:
{
"DatabaseOptions": {
"ConnectionString": "Server=localhost;Database=myapp;",
"MaxRetries": 3,
"CommandTimeout": 30
}
}// Automatically binds to "DatabaseOptions" section
services
.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(configuration)
.ValidateOnStart();Override the default by decorating your options class with [ConfigurationKey]:
Simple Configuration Key:
using TimeWarp.OptionsValidation;
[ConfigurationKey("Database")]
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
// ...
}Binds to "Database" section:
{
"Database": {
"ConnectionString": "Server=localhost;Database=myapp;",
"MaxRetries": 3,
"CommandTimeout": 30
}
}Hierarchical Key with Colon Separator:
[ConfigurationKey("MyApp:Settings:Database")]
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
// ...
}Binds to nested "MyApp" → "Settings" → "Database" path:
{
"MyApp": {
"Settings": {
"Database": {
"ConnectionString": "Server=localhost;Database=myapp;",
"MaxRetries": 3,
"CommandTimeout": 30
}
}
}
}// Automatically binds to configuration key specified in attribute
services
.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(configuration)
.ValidateOnStart();For dynamic section paths or complex scenarios not covered by the attribute:
// Manual binding for runtime-determined paths
string environment = builder.Environment.EnvironmentName;
services.AddOptions<DatabaseOptions>()
.Bind(configuration.GetSection($"{environment}:Database"))
.ValidateFluentValidation<DatabaseOptions, DatabaseOptions.Validator>()
.ValidateOnStart();Automatic Configuration Key Resolution Summary:
- ✅ Uses class name:
DatabaseOptions→"DatabaseOptions" - ✅ Simple override:
[ConfigurationKey("Database")]→"Database" - ✅ Hierarchical paths:
[ConfigurationKey("MyApp:Settings:Database")]→"MyApp" → "Settings" → "Database" - ❌ Does NOT trim suffixes like "Options" automatically
- ❌ Does NOT pluralize names automatically
You can also configure options programmatically without IConfiguration:
services
.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(options =>
{
options.ConnectionString = "Server=localhost;Database=myapp;";
options.MaxRetries = 3;
options.CommandTimeout = 30;
})
.ValidateOnStart();using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register multiple validated options with automatic startup validation
builder.Services
.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(builder.Configuration)
.ValidateOnStart();
builder.Services
.AddFluentValidatedOptions<CacheOptions, CacheOptions.Validator>(builder.Configuration)
.ValidateOnStart();
builder.Services
.AddFluentValidatedOptions<EmailOptions, EmailOptions.Validator>(builder.Configuration)
.ValidateOnStart();
var app = builder.Build();
app.Run(); // All options validated before this runsIf any configuration is invalid, the application will fail to start with clear error messages indicating exactly which settings are invalid and why.
If you don't need automatic startup validation, simply omit .ValidateOnStart():
// Validates on first access instead of at startup
services.AddFluentValidatedOptions<DatabaseOptions, DatabaseOptions.Validator>(configuration);
// No .ValidateOnStart() call - validation happens lazilyThis approach validates options when they're first accessed rather than at application startup.
- Automatic Startup Validation: Use
.ValidateOnStart()to fail fast on invalid configuration - Automatic Key Discovery: Uses the class name as the configuration key by default
- Custom Key Mapping: Use
[ConfigurationKey]attribute to override the configuration key - Hierarchical Keys: Support for nested configuration paths using colon separators
- Seamless Integration: Works with Microsoft.Extensions.Options infrastructure and
OptionsBuilder<T> - FluentValidation Power: Rich validation rules, custom validators, conditional validation
- Clear Error Messages: Detailed, actionable error messages from FluentValidation
- Type Safety: Strongly-typed options with compile-time checking
- Flexible API: Choose between fluent API (with
.ValidateOnStart()) or simple registration
See the Release Notes
Time is of the essence. Before developing a Pull Request I recommend opening a discussion.
Please feel free to make suggestions and help out with the documentation. Please refer to Markdown for how to write markdown files.
Sometimes the github notifications get lost in the shuffle. If you file an issue and don't get a response in a timely manner feel free to ping on our Discord server.
