Skip to content

[API Proposal]: Option validation features for IServiceCollection #106931

Open
@aetos382

Description

@aetos382

Background and motivation

To configure IOptions<T> for IServiceCollection, the OptionsServiceCollectionExtensions class provides the Configure / PostConfigure / ConfigureAll / PostConfigureAll methods.
But, there is no validation API except for AddOptionsWithValidateOnStart.
In particular, there is no function to configure validation for all TOptions types regardless of the option name (such as ValidateAll).

We can validate all TOptions by writing as follows.
But this is not documented and is not intuitive.

services.AddSingleton<IValidateOptions<TOptions>>(
    new ValidateOptions<TOptions>(
        name: null,
        validation: static options => /* validation logic */,
        failureMessage: "validation failed"));

API Proposal

namespace Microsoft.Extensions.DependencyInjection;

public static class OptionsServiceCollectionExtensions
{
    public static IServiceCollection Validate<TOptions>(this IServiceCollection services, Action<TOptions, ValidateOptionsResult> validation);
	public static IServiceCollection Validate<TOptions>(this IServiceCollection services, string? name, Action<TOptions, ValidateOptionsResult> validation);
	public static IServiceCollection ValidateAll<TOptions>(this IServiceCollection services, Action<TOptions, ValidateOptionsResult> validation);
}

API Usage

var services = new ServiceCollection();

services
    .Configure<MyOptions>(static options => /* configuration */)
    .Validate(static options =>
        MyValidationLogic(options, out var messages)
            ? ValidateOptionsResult.Success
            : ValidateOptionsResult.Fail(messages));

Alternative Designs

It is also possible to design the delegate to return a bool type instead of a ValidateOptionResult, and to take the error message in the case where its result is false as another argument (as in the constructor of the ValidateOptions<TOptions> class).

public static IServiceCollection Validate<TOptions>(this IServiceCollection services, Action<TOptions, bool> validation, string failureMessage);

Another possibility is the method names ValidateOptions and ValidateAllOptions. This is because the name Validate does not sufficiently express what is being validated for IServiceCollection.

However, this problem also exists with the existing Configure and PostConfigure. Therefore, those APIs will also be renamed to ConfigureOptions and PostConfigureOptions, and the existing APIs will be marked as [Obsolete].


There is a proposal to use OptionsBuilder<TOptions> to configure all options.

Currently, even if written as follows, only unnamed options can be configured, and it is not possible to configure both named and unnamed options with a single OptionsBuilder<TOptions>.

services.AddOptions<MyOptions>(null).Configure(...).Validate(...);

Since we can't change the behavior of AddOptions<MyOptions>(null), we'll probably introduce a new method like the following.

public static class OptionsServiceCollectionExtensions
{
    public static OptionsBuilder<TOptions> AddOptionsForAll<TOptions>(this IServiceCollection services);
}

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions