A C# source generator that automatically configures IOptions for classes marked with a custom attribute.
- Automatically generates
IServiceCollectionextension methods to configure options - Supports validation at startup via
ValidateOnStartproperty - Supports data annotations validation via
ValidateDataAnnotationsproperty - Configurable configuration section binding
- Automatic JSON schema generation for IDE intellisense and validation in appsettings.json
- Zero runtime overhead - all code is generated at compile time
- OptionsSourceGenerated: Example console application demonstrating usage
- OptionsSourceGenerated.Generator: The source generator project
- OptionsSourceGenerated.Generator.Tests: Comprehensive unit tests for the source generator
using System.ComponentModel.DataAnnotations;
[ConfigureOptions(ConfigurationSection = "Database", ValidateOnStart = true, ValidateDataAnnotations = true)]
public class DatabaseOptions
{
[Required(ErrorMessage = "Connection string is required")]
[MinLength(10, ErrorMessage = "Connection string must be at least 10 characters")]
public string ConnectionString { get; set; } = string.Empty;
[Range(0, 10, ErrorMessage = "Max retries must be between 0 and 10")]
public int MaxRetries { get; set; }
[Range(1, 300, ErrorMessage = "Timeout must be between 1 and 300 seconds")]
public int TimeoutSeconds { get; set; }
}
[ConfigureOptions(ConfigurationSection = "Api", ValidateOnStart = false, ValidateDataAnnotations = true)]
public class ApiOptions
{
[Required(ErrorMessage = "Base URL is required")]
[Url(ErrorMessage = "Base URL must be a valid URL")]
public string BaseUrl { get; set; } = string.Empty;
[Required(ErrorMessage = "API key is required")]
[MinLength(5, ErrorMessage = "API key must be at least 5 characters")]
public string ApiKey { get; set; } = string.Empty;
[Range(1, 1000, ErrorMessage = "Rate limit must be between 1 and 1000")]
public int RateLimitPerMinute { get; set; }
}{
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb;",
"MaxRetries": 3,
"TimeoutSeconds": 30
},
"Api": {
"BaseUrl": "https://api.example.com",
"ApiKey": "your-api-key-here",
"RateLimitPerMinute": 60
}
}var builder = Host.CreateApplicationBuilder(args);
// The source generator creates the AddGeneratedOptions extension method
builder.Services.AddGeneratedOptions(builder.Configuration);
var host = builder.Build();// Constructor injection
public class MyService
{
private readonly DatabaseOptions _dbOptions;
public MyService(IOptions<DatabaseOptions> dbOptions)
{
_dbOptions = dbOptions.Value;
}
}
// Or direct retrieval
var databaseOptions = host.Services.GetRequiredService<IOptions<DatabaseOptions>>().Value;The source generator automatically creates an extension method like this:
public static class OptionsConfigurationExtensions
{
public static IServiceCollection AddGeneratedOptions(this IServiceCollection services, IConfiguration configuration)
{
// Configure DatabaseOptions
services.AddOptions<OptionsSourceGenerated.DatabaseOptions>()
.Bind(configuration.GetSection("Database"))
.ValidateDataAnnotations()
.ValidateOnStart();
// Configure ApiOptions
services.AddOptions<OptionsSourceGenerated.ApiOptions>()
.Bind(configuration.GetSection("Api"))
.ValidateDataAnnotations();
return services;
}
}The configuration section name to bind from appsettings.json. Supports nested sections using colon notation (e.g., "Parent:Child").
When set to true, validates the options configuration at application startup. This uses the .ValidateOnStart() extension method, ensuring configuration errors are caught early.
When set to true, enables data annotations validation for the options class. This uses the .ValidateDataAnnotations() extension method, which validates properties decorated with attributes from System.ComponentModel.DataAnnotations (e.g., [Required], [Range], [MinLength], [Url], etc.).
The source generator automatically creates a JSON schema for all your options classes. This schema can be used for:
- IDE Intellisense: Get autocomplete and validation in appsettings.json
- Build-time validation: Catch configuration errors before runtime
- Documentation: Understand configuration structure and constraints
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Application Settings",
"type": "object",
"properties": {
"Database": {
"type": "object",
"required": ["ConnectionString"],
"properties": {
"ConnectionString": {
"type": "string",
"minLength": 10
},
"MaxRetries": {
"type": "integer",
"minimum": 0,
"maximum": 10
},
"TimeoutSeconds": {
"type": "integer",
"minimum": 1,
"maximum": 300
}
}
}
}
}The generated schema is available through the OptionsJsonSchema.Schema constant:
// Export schema to a file
File.WriteAllText("appsettings.schema.json", OptionsJsonSchema.Schema);
// Or use the helper
SchemaExporter.ExportSchema("appsettings.schema.json");
// Print to console
Console.WriteLine(OptionsJsonSchema.Schema);The schema is automatically generated during build and placed in your project directory next to appsettings.json. The post-build event runs:
dotnet run --no-build -- --export-schema-onlyThis creates/updates appsettings.schema.json in your project root.
- Add a
$schemaproperty to yourappsettings.json:
{
"$schema": "./appsettings.schema.json",
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb;",
"MaxRetries": 3,
"TimeoutSeconds": 30
}
}-
Rider will automatically detect the schema and provide:
- Code completion (Ctrl+Space) for property names
- Type validation with error highlighting
- Quick documentation (Ctrl+Q / Cmd+J) on hover
- Constraint validation for min/max values, string lengths, formats
-
Additional Rider Setup (if schema isn't detected):
- Go to Settings → Languages & Frameworks → Schemas and DTDs → JSON Schema Mappings
- Click + to add a new mapping
- Schema file:
appsettings.schema.json(in your project directory) - Schema version: JSON Schema version 7
- Add file pattern:
appsettings.jsonorappsettings.*.json
- Add
$schemato yourappsettings.json(same as above) - VS Code automatically supports JSON schemas via the
$schemaproperty - You'll get IntelliSense, validation, and hover information
- Add
$schemato yourappsettings.json(same as above) - Visual Studio 2019+ automatically supports JSON schemas
- Rebuild your project to regenerate the schema after changes
The IDE will now provide:
- Intellisense for property names
- Validation of property types
- Constraint validation (minLength, minimum, maximum, format)
- Hover documentation for properties
- Real-time error detection
The schema includes:
- PascalCase property names (matching C# conventions)
- Required properties: Properties with
[Required]attribute are listed in therequiredarray - Property types: string, integer, number, boolean, array, object
- Data annotation constraints:
[Required]→ Added torequiredarray[Range(min, max)]→minimumandmaximum[MinLength(n)]→minLength[MaxLength(n)]→maxLength[Url]→format: "uri"[EmailAddress]→format: "email"
- XML documentation comments as descriptions
- Nested configuration section support (e.g.,
"Parent:Child") - Clean JSON formatting with proper indentation
Note: Property names use PascalCase to match C# naming conventions and .NET configuration binding.
dotnet builddotnet run --project OptionsSourceGenerated/OptionsSourceGenerated.csprojThe project includes comprehensive unit tests for the source generator:
dotnet test OptionsSourceGenerated.Generator.Tests/OptionsSourceGenerated.Generator.Tests.csprojTest coverage includes:
- Attribute code generation
- Extension method generation with various validation combinations
- Multiple classes with different configurations
- Nested configuration sections
- Edge cases (missing configuration sections, special characters, Unicode, etc.)
- Partial classes, record classes, and nested classes
- The source generator scans for classes decorated with
[ConfigureOptions] - At compile time, it generates an extension method that registers all marked options classes
- The generated code uses:
services.Configure<T>()for simple binding (when no validation is enabled)services.AddOptions<T>().Bind().ValidateDataAnnotations()when data annotations validation is enabledservices.AddOptions<T>().Bind().ValidateDataAnnotations().ValidateOnStart()when both validations are enabled
- No reflection or runtime discovery - everything is resolved at compile time
- .NET 10.0 or later (can be adjusted in the project files)
- Microsoft.Extensions.Hosting 9.0.0+
- Microsoft.Extensions.Options.ConfigurationExtensions 9.0.0+
- Microsoft.Extensions.Options.DataAnnotations 9.0.0+ (required when using ValidateDataAnnotations)