IValidateOptions<TOptions> source generator new in .NET 8 versus existing reflection approach




Date Added (UTC):

08 Apr 2024 @ 00:03

Date Updated (UTC):

12 Apr 2024 @ 12:26


.NET Version(s):

.NET 8

Tag(s):

#.Net8PerfImprovement #Configuration #SourceGenerators


Added By:
Profile Image

Blog   
Ireland    
.NET Developer and tech lead from Ireland!

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes; 
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;

public partial class OptionsValidationSourceGenerator
{
    private readonly DataAnnotationValidateOptions<MyOptions> _davo = new DataAnnotationValidateOptions<MyOptions>(null);
    private readonly MyOptionsValidator _ov = new();
    private readonly MyOptions _options = new() { Path = "1234567890", Address = "http://localhost/path", PhoneNumber = "555-867-5309" };

    [Benchmark(Baseline = true)]
    public ValidateOptionsResult WithReflection() => _davo.Validate(null, _options);

    [Benchmark]
    public ValidateOptionsResult WithSourceGen() => _ov.Validate(null, _options);

    public sealed class MyOptions
    {
        [Length(1, 10)]
        public string Path { get; set; }

        [Url]
        public string Address { get; set; }

        [Phone]
        public string PhoneNumber { get; set; }
    }

    [OptionsValidator]
    public partial class MyOptionsValidator : IValidateOptions<MyOptions> { }
}

// .NET 8 Lowered C# Code unavailable due to errors:
error CS0234: The type or namespace name 'DataAnnotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Options' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'ValidateOptionsResult' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DataAnnotationValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'IValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'LengthAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Length' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'UrlAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Url' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'PhoneAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Phone' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidatorAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidator' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'OptionsValidationSourceGenerator.MyOptionsValidator' does not contain a definition for 'Validate' and no accessible extension method 'Validate' accepting a first argument of type 'OptionsValidationSourceGenerator.MyOptionsValidator' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 IL Code unavailable due to errors:
error CS0234: The type or namespace name 'DataAnnotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Options' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'ValidateOptionsResult' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DataAnnotationValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'IValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'LengthAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Length' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'UrlAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Url' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'PhoneAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Phone' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidatorAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidator' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'OptionsValidationSourceGenerator.MyOptionsValidator' does not contain a definition for 'Validate' and no accessible extension method 'Validate' accepting a first argument of type 'OptionsValidationSourceGenerator.MyOptionsValidator' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 Jit Asm Code unavailable due to errors:
error CS0234: The type or namespace name 'DataAnnotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Options' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'ValidateOptionsResult' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DataAnnotationValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'IValidateOptions<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'LengthAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Length' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'UrlAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Url' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'PhoneAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Phone' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidatorAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'OptionsValidator' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'OptionsValidationSourceGenerator.MyOptionsValidator' does not contain a definition for 'Validate' and no accessible extension method 'Validate' accepting a first argument of type 'OptionsValidationSourceGenerator.MyOptionsValidator' could be found (are you missing a using directive or an assembly reference?)


Benchmark Description:


_Microsoft.Extensions.Options.DataAnnotationValidateOptions provides an implementation of the IValidateOptions<TOptions> interface (an implementation of which is typically retrieved via DI) for validating models based on data annotations, and as you can probably guess, it does so via reflection. As is a trend you’re probably picking up on, for many such areas involving reflection, .NET has been moving to add source generators that can do at build-time what would have otherwise been done at run-time; that’s the case here as well. As of dotnet/runtime#87587, the Microsoft.Extensions.Options package in .NET 8 now includes a source generator that creates an implementation of IValidateOptions<TOptions> for a specific TOptions type._ https://github.com/dotnet/runtime/pull/87587

The benchmark code you've provided is designed to compare the performance of two different approaches for validating options in a .NET application. These approaches are validation using reflection (a more traditional method) and validation using a source generator (a newer, compile-time technique). This comparison is particularly relevant in the context of applications that rely heavily on options validation for configuration settings, where performance and efficiency can significantly impact overall application responsiveness and resource consumption. ### General Setup - **.NET Version**: The specific .NET version isn't mentioned, but given the use of source generators, it's implied that the benchmark targets .NET 5 or newer, as source generators were introduced in .NET 5. - **Configuration**: The benchmark is set up using BenchmarkDotNet, a popular and powerful library for benchmarking .NET code. The configuration details for BenchmarkDotNet (such as the runtime, JIT version, etc.) are not explicitly mentioned, but BenchmarkDotNet typically provides a detailed output that includes this information. - **Classes and Validators**: The benchmark involves a `MyOptions` class with three properties, each annotated with data annotations (`[Length]`, `[Url]`, and `[Phone]`) for validation purposes. Two validation approaches are compared: - **Reflection-based validation** (`DataAnnotationValidateOptions<T>`), which uses reflection to inspect data annotations at runtime. - **Source generator-based validation** (`MyOptionsValidator`), which generates validation code at compile time, potentially reducing the runtime overhead associated with reflection. ### Benchmark Methods #### 1. `WithReflection()` - **Purpose**: This method measures the performance of validating the `MyOptions` instance using reflection-based validation (`DataAnnotationValidateOptions<T>`). - **Performance Aspect**: It tests the overhead of using reflection to inspect data annotations and perform validation at runtime. Reflection is known to be relatively slow and resource-intensive compared to compile-time techniques. - **Expected Insights**: Running this benchmark will give you an idea of how long it takes and how much resource is consumed when using reflection for options validation. This serves as the baseline for comparison. #### 2. `WithSourceGen()` - **Purpose**: This method evaluates the performance of validating the `MyOptions` instance using a source generator-based approach (`MyOptionsValidator`). - **Performance Aspect**: It aims to quantify the benefits of using compile-time code generation for validation over runtime reflection. Since the validation logic is generated at compile time, it should, in theory, be faster and less resource-intensive. - **Expected Insights**: The results from this benchmark will highlight the efficiency gains from avoiding reflection. Ideally, this method should be faster and consume fewer resources than the reflection-based approach, demonstrating the advantages of compile-time validation. ### Importance and Results Interpretation The comparison between these two validation methods is crucial for understanding the performance implications of using reflection versus compile-time code generation in .NET applications. Reflection, while powerful and flexible, can introduce significant performance penalties, especially in critical paths of an application. Source generators, on the other hand, offer a way to mitigate these penalties by moving the computational cost to compile time. When interpreting the results: - **Lower execution times and resource usage** for the `WithSourceGen()` method would validate the hypothesis that source generators offer a more performant alternative to reflection-based validation. - **Understanding the trade-offs** is also important. While source generators can improve performance, they also introduce complexity in terms of development and debugging. This benchmark setup provides a clear comparison between two common approaches to options validation in .NET, offering valuable insights into how to optimize configuration validation in .NET applications.


Benchmark Comments: