Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public Host(IServiceProvider services,
/// Order:
/// IHostLifetime.WaitForStartAsync
/// Services.GetService{IStartupValidator}().Validate()
/// Services.GetService{IAsyncStartupValidator}().ValidateAsync()
/// IHostedLifecycleService.StartingAsync
/// IHostedService.Start
/// IHostedLifecycleService.StartedAsync
Expand Down Expand Up @@ -96,12 +97,31 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
_hostedServices ??= Services.GetRequiredService<IEnumerable<IHostedService>>();
_hostedLifecycleServices = GetHostLifecycles(_hostedServices);

// Call startup validators.
// Two-stage startup validation:
// Stage 1 (sync): Run IStartupValidator.Validate() — iterates _validators dictionary
// (or user's custom implementation if registered).
// If sync validation fails, skip async to avoid expensive I/O on invalid config.
// Stage 2 (async): Run IAsyncStartupValidator.ValidateAsync() — iterates _asyncValidators
// dictionary (or user's custom implementation if registered).
//
// Each interface is resolved independently via DI. TryAddTransient semantics ensure
// user-registered implementations replace the built-in for each interface separately.
IStartupValidator? validator = Services.GetService<IStartupValidator>();
validator?.Validate();

IAsyncStartupValidator? asyncValidator = Services.GetService<IAsyncStartupValidator>();
if (asyncValidator is not null)
{
await asyncValidator.ValidateAsync(cancellationToken).ConfigureAwait(false);
Comment thread
ViveliDuCh marked this conversation as resolved.
}
}
catch (Exception ex)
{
if (ex is OperationCanceledException)
{
cancellationToken.ThrowIfCancellationRequested();
}

// service factory or validation failed, abort startup.
Comment thread
ViveliDuCh marked this conversation as resolved.
exceptions.Add(ex);
LogAndRethrow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,18 @@ public partial interface IStartupValidator
{
void Validate();
}
public partial interface IAsyncStartupValidator
{
System.Threading.Tasks.Task ValidateAsync(System.Threading.CancellationToken cancellationToken = default);
}
public partial interface IValidateOptions<TOptions> where TOptions : class
{
Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, TOptions options);
}
public partial interface IAsyncValidateOptions<in TOptions> where TOptions : class
{
System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default);
}
public static partial class Options
{
public static readonly string DefaultName;
Expand Down Expand Up @@ -184,6 +192,18 @@ public OptionsBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollectio
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, bool> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4, TDep5>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, bool> validation) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull where TDep5 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4, TDep5>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, bool> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull where TDep5 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate(System.Func<TOptions, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate(System.Func<TOptions, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep>(System.Func<TOptions, TDep, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) where TDep : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep>(System.Func<TOptions, TDep, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) where TDep : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2>(System.Func<TOptions, TDep1, TDep2, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) where TDep1 : notnull where TDep2 : notnull { throw null; }
Comment thread
ViveliDuCh marked this conversation as resolved.
Comment on lines +195 to +199
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2>(System.Func<TOptions, TDep1, TDep2, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3>(System.Func<TOptions, TDep1, TDep2, TDep3, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3>(System.Func<TOptions, TDep1, TDep2, TDep3, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4, TDep5>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull where TDep5 : notnull { throw null; }
public virtual Microsoft.Extensions.Options.OptionsBuilder<TOptions> Validate<TDep1, TDep2, TDep3, TDep4, TDep5>(System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) where TDep1 : notnull where TDep2 : notnull where TDep3 : notnull where TDep4 : notnull where TDep5 : notnull { throw null; }
Comment thread
ViveliDuCh marked this conversation as resolved.
}
public partial class OptionsCache<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : Microsoft.Extensions.Options.IOptionsMonitorCache<TOptions> where TOptions : class
{
Expand Down Expand Up @@ -399,4 +419,67 @@ public ValidateOptions(string? name, TDep1 dependency1, TDep2 dependency2, TDep3
public System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, bool> Validation { get { throw null; } }
public Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, TOptions options) { throw null; }
}
public partial class AsyncValidateOptions<TOptions> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, System.Func<TOptions, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial class AsyncValidateOptions<TOptions, TDep> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, TDep dependency, System.Func<TOptions, TDep, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public TDep Dependency { get { throw null; } }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, TDep, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial class AsyncValidateOptions<TOptions, TDep1, TDep2> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, TDep1 dependency1, TDep2 dependency2, System.Func<TOptions, TDep1, TDep2, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public TDep1 Dependency1 { get { throw null; } }
public TDep2 Dependency2 { get { throw null; } }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, TDep1, TDep2, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial class AsyncValidateOptions<TOptions, TDep1, TDep2, TDep3> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, TDep1 dependency1, TDep2 dependency2, TDep3 dependency3, System.Func<TOptions, TDep1, TDep2, TDep3, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public TDep1 Dependency1 { get { throw null; } }
public TDep2 Dependency2 { get { throw null; } }
public TDep3 Dependency3 { get { throw null; } }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, TDep1, TDep2, TDep3, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial class AsyncValidateOptions<TOptions, TDep1, TDep2, TDep3, TDep4> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, TDep1 dependency1, TDep2 dependency2, TDep3 dependency3, TDep4 dependency4, System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public TDep1 Dependency1 { get { throw null; } }
public TDep2 Dependency2 { get { throw null; } }
public TDep3 Dependency3 { get { throw null; } }
public TDep4 Dependency4 { get { throw null; } }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
public partial class AsyncValidateOptions<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5> : Microsoft.Extensions.Options.IAsyncValidateOptions<TOptions> where TOptions : class
{
public AsyncValidateOptions(string? name, TDep1 dependency1, TDep2 dependency2, TDep3 dependency3, TDep4 dependency4, TDep5 dependency5, System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> validation, string failureMessage) { }
public TDep1 Dependency1 { get { throw null; } }
public TDep2 Dependency2 { get { throw null; } }
public TDep3 Dependency3 { get { throw null; } }
public TDep4 Dependency4 { get { throw null; } }
public TDep5 Dependency5 { get { throw null; } }
public string FailureMessage { get { throw null; } }
public string? Name { get { throw null; } }
public System.Func<TOptions, TDep1, TDep2, TDep3, TDep4, TDep5, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>> Validation { get { throw null; } }
public System.Threading.Tasks.Task<Microsoft.Extensions.Options.ValidateOptionsResult> ValidateAsync(string? name, TOptions options, System.Threading.CancellationToken cancellationToken = default) { throw null; }
}
}
Loading
Loading