Using the ValidateOptionsResultBuilder class – Options, Settings, and Configuration

The ValidateOptionsResultBuilder is a new type in .NET 8. It allows to dynamically accumulate validation errors and create a ValidateOptionsResult object representing its current state.Its basic usage is straightforward, as we are about to see.

Project – ValidateOptionsResultBuilder

In this project, we are validating the MyOptions object. The type has multiple validation rules, and we want to ensure we are not stopping after the first rule fails validation so a consumer would know all the errors in one go. To achieve this, we decided to use the ValidateOptionsResultBuilder class.Let’s start with the options class:

namespace ValidateOptionsResultBuilder;
public class MyOptions
{
    public string?
Prop1 { get; set; }
    public string?
Prop2 { get; set; }
}

Next, let’s implement a validator class that enforces both properties are not empty:

using Microsoft.Extensions.Options;
namespace ValidateOptionsResultBuilder;
public class SimpleMyOptionsValidator : IValidateOptions<MyOptions>
{
    public ValidateOptionsResult Validate(string?
name, MyOptions options)
    {
        var builder = new Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
        if (string.IsNullOrEmpty(options.Prop1))
        {
            builder.AddError(
                “The value cannot be empty.”,
                nameof(options.Prop1)
            );
        }
        if (string.IsNullOrEmpty(options.Prop2))
        {
            builder.AddError(
                “The value cannot be empty.”,
                nameof(options.Prop2)
            );
        }
        return builder.Build();
    }
}

In the preceding code, we create a ValidateOptionsResultBuilder object, add errors to it, then returns an instance of the SimpleMyOptionsValidator class by leveraging its Build method. The usage of the ValidateOptionsResultBuilder class is highlighted.Next, to test this out, we must register the options. Let’s also create an endpoint. Here’s the Program.cs file:

using ValidateOptionsResultBuilder;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services
    .AddSingleton<IValidateOptions<MyOptions>, SimpleMyOptionsValidator>()
    .AddOptions<MyOptions>(“simple”)
    .BindConfiguration(“SimpleMyOptions”)
    .ValidateOnStart()
;
var app = builder.Build();
app.MapGet(“/”, (IOptionsFactory<MyOptions> factory) => new
{
    simple = factory.Create(“simple”)
});
app.Run();

The preceding code is as normal as it can get after a whole chapter on the Options pattern. We register our options class, the validator, and create an endpoint.When we call the endpoint, we get the following result:

Hosting failed to start
Microsoft.Extensions.Options.OptionsValidationException: Property Prop1: The value cannot be empty.; Property Prop2: The value cannot be empty.

As expected, the application failed to start because the validation of the MyOptions class failed. One difference is that we have two combined error messages instead of one.As a reference, a validator doing the same without using the ValidateOptionsResultBuilder type would look like this:

using Microsoft.Extensions.Options;
namespace ValidateOptionsResultBuilder;
public class ClassicMyOptionsValidator : IValidateOptions<MyOptions>
{
    public ValidateOptionsResult Validate(string?
name, MyOptions options)
    {
        if (string.IsNullOrEmpty(options.Prop1))
        {
            return ValidateOptionsResult.Fail(
                $”Property {nameof(options.Prop1)}: The value cannot be empty.”
           
);
        }
        if (string.IsNullOrEmpty(options.Prop2))
        {
            return ValidateOptionsResult.Fail(
                $”Property {nameof(options.Prop2)}: The value cannot be empty.”
           
);
        }
        return ValidateOptionsResult.Success;
    }
}

The highlighted code represents the standard process which get replaced by the use of the ValidateOptionsResultBuilder type in the SimpleMyOptionsValidator class.This concludes our project. Nothing very complex, yet it is a nice addition to help accumulate multiple error messages. On top of that, the ValidateOptionsResultBuilder type can also accumulate ValidationResult and ValidateOptionsResult objects which can lead to more complex systems like collecting results from multiple validators. I’ll let you fiddle with this one.Let’s recap this chapter before jumping into ASP.NET Core logging.

Leave a Reply

Your email address will not be published. Required fields are marked *



         


          Terms of Use | Accessibility Privacy