Skip to content

Options validation source generator uses named option name as type name #115496

Open
@sbomer

Description

@sbomer

When a [Required] property is missing, the DataAnnotations validator produces an error like: The <propertyname> field is required. When using named options with the validation source generator, it instead produces an error like <propertyname>: the <optionname>.<propertyname> field is required.

This looks wrong because the optionname should not be used in place of a type name. It would also be nice if it matched the old error message.

using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

public class AppSettings
{
    [Required]
    public string ConnectionString { get; set; }
}

public class AppSettingsValidator : IValidateOptions<AppSettings>
{
    public ValidateOptionsResult Validate(string name, AppSettings options)
    {
        var validationContext = new ValidationContext(options);
        var results = new System.Collections.Generic.List<ValidationResult>();

        bool isValid = Validator.TryValidateObject(
            options, validationContext, results, validateAllProperties: true);

        if (isValid)
        {
            return ValidateOptionsResult.Success;
        }

        var errors = new System.Text.StringBuilder();
        foreach (var result in results)
        {
            errors.AppendLine(result.ErrorMessage);
        }

        return ValidateOptionsResult.Fail(errors.ToString());
    }
}

class Program
{
    static void Main()
    {
        var services = new ServiceCollection();

        services.AddOptions<AppSettings>("MySettings")
            .Configure(options =>
            {
            })
            .Services.AddSingleton<IValidateOptions<AppSettings>, AppSettingsValidator>();

        var provider = services.BuildServiceProvider();

        var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<AppSettings>>();

        try
        {
            var options = optionsMonitor.Get("MySettings");
            Console.WriteLine($"ConnectionString: {options.ConnectionString}");
        }
        catch (OptionsValidationException ex)
        {
            Console.WriteLine("Validation failed:");
            foreach (var failure in ex.Failures)
            {
                Console.WriteLine($"- {failure}");
            }
        }
    }
}

This shows:

Validation failed:
- The ConnectionString field is required.

If I change the repro to use the source generator:

[OptionsValidator]
public partial class AppSettingsValidator : IValidateOptions<AppSettings>
{
}

It shows:

Validation failed:
- ConnectionString: The MySettings.ConnectionString field is required.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions