dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.38k stars 4.75k forks source link

[API Proposal]: Expose bound configuration sections (so it is possible to generate a JSON schema for appsettings.json) #94697

Open amoerie opened 1 year ago

amoerie commented 1 year ago

Background and motivation

This might be a little ambitious, but I think it would add tremendous value if .NET applications would have the ability to automatically generate a JSON schema based on the bound configuration sections (via ConfigureOptions). It feels almost weird to have all these tools in our toolbelt to enforce strong typing in C#, but when it comes to configuration all bets are off, and we are back in stringly typed JSON files with no assistance or guidance whatsoever.

I know that an effort was made to provide a very basic JSON schema in SchemaStore here, but that only contains a few of the possible sections, does not update automatically and does not include any user defined configuration sections.

The most realistic approach developers can take today is take the template from SchemaStore and extend it manually with their custom configuration sections. I still consider that approach flawed, because it is prone to human error and does not update automatically with what exists at runtime. Furthermore, it is duplicate effort to 1) write XML documentation on the C# types and 2) do the same in appsettings.schema.json

I am currently in the process of trying to automatically generate a JSON schema, but the main hurdle I'm still facing is determining which configuration sections are linked to which options types. As far as I can find, this information is never collected in a single way, so I'm having to jump through A LOT of hoops to do so. Some sections (Kestrel for example) are downright impossible to discover automatically.

My proposal is to somehow expose which Options types are linked to which configuration sections, which would then allow to generate a JSON schema using some of the open source libraries that can do so.

Possible open source libraries:

API Proposal

I'm not sure if this API approach makes the most sense, but this would be very convenient at least:

namespace Microsoft.Extensions.Configuration
{
    public interface IConfigurationSection : IConfiguration
    {
        /// <summary>
        /// Gets the types that are bound to this configuration section
        /// </summary>
        IEnumerable<Type> BoundTypes { get; }
    }
}

API Usage

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOptions<RabbitMqOptions>().BindConfiguration("RabbitMq");
var application = builder.Build();
foreach(var type in application.Configuration.GetSection("RabbitMq").BoundTypes)
{
  Console.WriteLine(type); // Should print RabbitMqOptions
}

public record RabbitMqOptions {}

Alternative Designs

I'm not sure. Any API that allows me to retrieve which C# types are bound against which configuration sections would be a tremendous help.

Edit: this is what I have today: https://github.com/amoerie/configo/blob/main/src/Configo.Client/JsonSchemaGenerator/BoundConfigurationSectionsDiscoverer.cs

It requires capturing the ServiceCollection to use at runtime, and then invoking all IConfigureOptions via reflection while recording which configuration sections are called from within. After all of this hassle, this still only works for the case where you use BindConfiguration("..."). If you capture the section beforehand during application startup (which is what 90% of the existing code seems to do), this discovery still fails.

I believe that a comprehensive solution will require modifications to ConfigurationRoot and ConfigurationSection themselves, so that they can keep track of which objects they're bound against.

Risks

As far as I can see, Microsoft.Extensions.Options is not built to support the registration of bound types, so it might be a considerable refactor to make this possible.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-extensions-configuration See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation This might be a little ambitious, but I think it would add tremendous value if .NET applications would have the ability to automatically generate a JSON schema based on the bound configuration sections (via `ConfigureOptions`). It feels almost weird to have all these tools in our toolbelt to enforce strong typing in C#, but when it comes to configuration all bets are off, and we are back in stringly typed JSON files with no assistance or guidance whatsoever. I know that an effort was made to provide a very basic JSON schema in SchemaStore [here](https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/appsettings.json), but that only contains a few of the possible sections, does not update automatically and does not include any user defined configuration sections. I am currently in the process of trying to automatically generate a JSON schema, but the main hurdle I'm still facing is determining which configuration sections are linked to which options types. As far as I can find, this information is never collected in a single way, so I'm having to jump through A LOT of hoops to do so. Some sections (Kestrel for example) are downright impossible to discover automatically. My proposal is to somehow expose which Options types are linked to which configuration sections, which would then allow to generate a JSON schema using some of the open source libraries that can do so. Possible open source libraries: - https://github.com/RicoSuter/NJsonSchema (does not support draft 7 and up though) - https://github.com/gregsdennis/json-everything (does not support XML docs) ### API Proposal I'm not sure if this API approach makes the most sense, but this would be very convenient at least: ```csharp namespace Microsoft.Extensions.Configuration { public interface IConfigurationSection : IConfiguration { /// /// Gets the types that are bound to this configuration section /// IEnumerable BoundTypes { get; } } } ``` ### API Usage ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddOptions().BindConfiguration("RabbitMq"); var application = builder.Build(); foreach(var type in application.Configuration.GetSection("RabbitMq").BoundTypes) { Console.WriteLine(type); // Should print RabbitMqOptions } public record RabbitMqOptions {} ``` ### Alternative Designs I'm not sure. Any API that allows me to retrieve which C# types are bound against which configuration sections would be a tremendous help. ### Risks As far as I can see, Microsoft.Extensions.Options is not built to support the registration of bound types, so it might be a considerable refactor to make this possible.
Author: amoerie
Assignees: -
Labels: `api-suggestion`, `area-Extensions-Configuration`
Milestone: -
KennethHoff commented 5 months ago

With .Net 9 and Json Schema support, being able to define one in appsettings is a natural next step.