aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

ValidateOnStart doesn't validate Options on startup

Open efie45 opened this issue 1 year ago • 4 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

In Program.cs I have the following section of code:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services
        .AddOptions<Settings>()
        .Bind(builder.Configuration.GetSection("Settings"))
        .Validate(_ =>
        {
            Console.WriteLine("Validating options");
            return false;
        })
        .ValidateOnStart();

However, validation of Settings does not occur until IOptions<Settings> is first injected when the first web request is receivied, or if the launch configuration is set to launch a browser with the swagger page.

Expected Behavior

ValidateOnStart should trigger the .Validate() validation to run on app startup

Steps To Reproduce

  1. Create a new .NET 8 Web Project using the 'Web API' template
  2. Edit Program.cs to contain the following code:
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddOptions<Settings>()
    .Bind(builder.Configuration.GetSection("Settings"))
    .Validate(_ =>
    {
        Console.WriteLine("Validating options");
        return false;
    })
    .ValidateOnStart();

var app = builder.Build();

app.UseHttpsRedirection();

app.Run();

public record Settings
{
    public required string TestProperty { get; init; }
}
  1. Modify launchSettings.json to contain the following (remove launch of swagger on startup from the configuration):
{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:1232",
      "sslPort": 44334
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Exceptions (if any)

No response

.NET Version

8.0.205

Anything else?

.NET SDK:
 Version:           8.0.205
 Commit:            3e1383b780
 Workload version:  8.0.200-manifests.818b3449

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22621
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\8.0.205\

.NET workloads installed:
There are no installed workloads to display.

Host:
  Version:      8.0.5
  Architecture: x64
  Commit:       087e15321b

.NET SDKs installed:
  6.0.423 [C:\Program Files\dotnet\sdk]
  8.0.105 [C:\Program Files\dotnet\sdk]
  8.0.205 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.31 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.31 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 6.0.31 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.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

efie45 avatar Jun 25 '24 18:06 efie45

I'm having the same issue myself, @efie45 are you able to solve this by any means?

bingbeann avatar Aug 12 '24 10:08 bingbeann

@bitlab-ruizhi, I left this open in case someone saw it and had suggestions but I believe the issue is that by default, IIS doesn't start up the project until the first request is initiated by design. So your 'validateOnStartup' call does eventually run, but not until a request first comes in. AFAIK there are possibly settings in IIS to 'run' the app on startup which would trigger the validation, but it's not available in IISExpress.

efie45 avatar Aug 12 '24 16:08 efie45

@bitlab-ruizhi @efie45 Catching up on some issue triage here. Can you verify if you are able to repro this in 8.0.400?

Here's the behavior I see for the provided repro:

$ dotnet run
Building...
Validating options
fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      Microsoft.Extensions.Options.OptionsValidationException: A validation error has occurred.
         at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
         at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
         at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
         at System.Lazy`1.CreateValue()
         at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd[TArg](String name, Func`3 createOptions, TArg factoryArgument)
         at Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.<>c__DisplayClass0_1`1.<ValidateOnStart>b__1()
         at Microsoft.Extensions.Options.StartupValidator.Validate()
      --- End of stack trace from previous location ---
         at Microsoft.Extensions.Options.StartupValidator.Validate()
         at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: A validation error has occurred.
   at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd[TArg](String name, Func`3 createOptions, TArg factoryArgument)
   at Microsoft.Extensions.DependencyInjection.OptionsBuilderExtensions.<>c__DisplayClass0_1`1.<ValidateOnStart>b__1()
   at Microsoft.Extensions.Options.StartupValidator.Validate()
--- End of stack trace from previous location ---
   at Microsoft.Extensions.Options.StartupValidator.Validate()
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at Program.<Main>$(String[] args) in /Users/captainsafia/git/tests/issue-56453/Program.cs:line 17

captainsafia avatar Aug 19 '24 23:08 captainsafia

Went with the exact same project setup as mentioned above. From my testing, the bug only happen when you use Visual Studio Debug mode and start with debugging (F5). In this particular setup, the program will start without any problem when inspected from the output from the project itself, which is {ProjectName} - ASP.NET Core Web Server

bingbeann avatar Aug 27 '24 14:08 bingbeann

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

See our Issue Management Policies for more information.