ValidateOnStart() and [Required] don't seem to work

43 views Asked by At

I have an appsettings.json with the following contents:

{
    "Settings": {
        "Greeting":  "Hello World"
    }
}

that I associate with

public sealed class SettingsOptions
{
    public const string SECTION = "Settings";

    [Required(ErrorMessage = "{0} is required.")]
    public string Greeting { get; init; } = default!;

    [Required(ErrorMessage = "{0} is required.")]
    [Range(1, 10, ErrorMessage = "{0} must be between {1} and {2} inclusive.")]
    public int Age { get; init; }
}

Note: I intentionally removed Age in appsettings.json to simulate an invalid case.

I expect my console app below throws an exception but apparently it does not when the app starts.

var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();

IServiceCollection services = new ServiceCollection();

services.AddSingleton<IConfiguration>(configuration);
services.AddOptions<SettingsOptions>()
        .BindConfiguration(SettingsOptions.SECTION)
        .ValidateDataAnnotations()
        .ValidateOnStart();


var provider = services.BuildServiceProvider();

#if false // only for simulation
var settings = provider.GetRequiredService<IOptions<SettingsOptions>>().Value;
Console.WriteLine($"greeting: {settings.Greeting}, age: {settings.Age}.");
#endif

Console.WriteLine("Done");
Console.ReadKey();

public sealed class SettingsOptions
{
    public const string SECTION = "Settings";

    [Required(ErrorMessage = "{0} is required.")]
    public string Greeting { get; init; } = default!;

    [Required(ErrorMessage = "{0} is required.")]
    [Range(1, 10, ErrorMessage = "{0} must be between {1} and {2} inclusive.")]
    public int Age { get; init; }
}

It does throw an exception if I enable

#if true// only for simulation
var settings = provider.GetRequiredService<IOptions<SettingsOptions>>().Value;
Console.WriteLine($"greeting: {settings.Greeting}, age: {settings.Age}.");
#endif

I have two questions:

  • Why doesn't ValidateOnStart() throw an exception before other parts need to resolve SettingsOptions?
  • Why doesn't [Required] have any effect (it does not throw an exception when Age in appsettings.json is missing but instead it is set to its default value 0)?

Edit:

<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
1

There are 1 answers

0
J.Memisevic On BEST ANSWER

It won't work inside the console app unless you implement the generic host in the console app. ValidateOnStart method is adding ValidationHostedService which is IHostedService which is triggered on host.Run().