Description
Description
Generated option validators do not check the contents of Dictionary<K, V>
types.
Reproduction Steps
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
var appBuilder = Host.CreateApplicationBuilder(args);
var configuration = appBuilder.Configuration;
configuration["AppOptions:ComplexOptions:Foo:Value"] = "1";
configuration["AppOptions:ComplexOptions:Bar:Value"] = "200";
var services = appBuilder.Services;
services
.AddOptionsWithValidateOnStart<AppOptions, AppOptionsValidator>()
.BindConfiguration("AppOptions");
using var host = appBuilder.Build();
await host.StartAsync().ConfigureAwait(false);
var appOptions = host.Services.GetRequiredService<IOptions<AppOptions>>().Value;
foreach (var (key, value) in appOptions.ComplexOptions)
{
Console.WriteLine($"{key}={value.Value}");
}
await host.StopAsync().ConfigureAwait(false);
class AppOptions
{
[Required]
[ValidateObjectMembers]
[ValidateEnumeratedItems]
public Dictionary<string, NestedOptions> ComplexOptions { get; set; } = [];
}
class NestedOptions
{
[Range(1, 100)]
public int Value { get; set; }
}
[OptionsValidator]
partial class AppOptionsValidator :
IValidateOptions<AppOptions>
{
}
Expected behavior
A validation error occurs for AppOptions:ComplexOptions:Bar:Value
.
Actual behavior
No validation errors occurred.
Regression?
No response
Known Workarounds
class AppOptions
{
[Required]
- [ValidateObjectMembers]
- [ValidateEnumeratedItems]
public Dictionary<string, NestedOptions> ComplexOptions { get; set; } = [];
+ [ValidateEnumeratedItems]
+ public ICollection<NestedOptions> ComplexOptionValues => ComplexOptions.Values;
}
Configuration
project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
</ItemGroup>
</Project>
dotnet --info
PS> dotnet --info.NET SDK:
Version: 9.0.300
Commit: 15606fe0a8
Workload version: 9.0.300-manifests.87b8cca8
MSBuild version: 17.14.5+edd3bbf37
Runtime Environment:
OS Name: Windows
OS Version: 10.0.26100
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\9.0.300\
.NET workloads installed:
[android]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 35.0.61/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.android\35.0.61\WorkloadManifest.json
Install Type: Msi
[aspire]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 8.2.2/8.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.2\WorkloadManifest.json
Install Type: Msi
[ios]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 18.4.9289/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.ios\18.4.9289\WorkloadManifest.json
Install Type: Msi
[maccatalyst]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 18.4.9289/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.maccatalyst\18.4.9289\WorkloadManifest.json
Install Type: Msi
[maui-windows]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 9.0.51/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.sdk.maui\9.0.51\WorkloadManifest.json
Install Type: Msi
[wasm-tools]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 9.0.5/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.workload.mono.toolchain.current\9.0.5\WorkloadManifest.json
Install Type: Msi
[wasm-tools-net8]
Installation Source: SDK 9.0.300, VS 17.14.36109.1
Manifest Version: 9.0.5/9.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\9.0.100\microsoft.net.workload.mono.toolchain.net8\9.0.5\WorkloadManifest.json
Install Type: Msi
Configured to use loose manifests when installing new manifests.
Host:
Version: 9.0.5
Architecture: x64
Commit: e36e4d1
.NET SDKs installed:
9.0.300 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
Other information
No response