You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Why should we use IOptions<T> when we can inject the configuration object directly? #22886
There is no information on this page about why the IOptions<T> interface exists, what purpose it serves, or why we should need to inject an IOptions<T> instance instead of a T instance for accessing configuration.
For comparison, a similar example in Java Spring Boot would look like this:
Note that the parameter to the model constructor is the type PositionOptions; there is no need for any wrapper type.
There just doesn't seem to be any value in wrapping the actual options type in IOptions<>. But I want to believe there is a rationale for this that I am missing, and I would love to see that rationale added to this page.
Thank you so much for posting this issue. We really appreciate it. That is a fair question to ask, however it is more of a product team question than it is a docs question. I suppose the docs could add this information, assuming that level of detail was readily available.
This was an architectural and API decision that would have been made a long time ago now. I believe that it is part of a larger pattern, where interfaces are preferred for dependency injection over concrete classes. The options pattern was specifically designed to function with a set of well defined interfaces:
Wrapping the configurations in interfaces allows for reloading of the underlying data-bound settings instances, I'm not sure how that would work otherwise.
Why should we use IOptions when we can inject the configuration object directly?
This is the easy question. Objects are nicer to deal with than strings and converting those strings to various data types in C#. You can divorce the consumption of your poco from how that poco is created. I think it's fine to start by grabbing configuration and readings things from it but ideally as your project grows in complexity, you'd want to move all of the code that goes from IConfiguration -> poco in a single place.
There is no information on this page about why the IOptions interface exists, what purpose it serves, or why we should need to inject an IOptions instance instead of a T instance for accessing configuration.
This comes up ALOT so I'm happy to provide something in the docs so we at least have something to point to. Using a generic wrapper type gives us the ability to decouple the lifetime of the option from the DI container. That has a couple of uses:
Being able to defer evaluation of something. In this case, the options infrastructure runs when accessed vs when resolved in a constructor. This is important because you can consume the T option from various places and chose the lifetime semantics without changing anything about T.
For IOptions<T> deferring resolution until the Value property is accessed.
For IOptionsSnapshot<T> to cache the most recent value of T within a scoped lifetime
For IOptionsMonitor<T> to resolve the most recent value of T whenever it changes (due to a configuration change)
The other advantage using the wrapper type has is it means you don't need to register the wrapped type (T in this case). We do this in a bunch of places where we have services consume IOptions<T> without explicit registration of T and consumption works. This is convenient if you're authoring a library with simple defaults where you don't want to force the caller to register options into the DI container with a specific lifetime.
It allows us to put constraints on T (for example it can only be a reference type), or that the members need to be preserved when linking. This is more advanced but it goes back to the idea that the T isn't doesn't need to be modified to describe things about it. That's what the wrapper type is for.
Now IOptions<T> vs T? If you choose to inject T directly into your types directly:
You need to register them (you can actually piggyback off the options infrastructure here).
You need to pick a lifetime. If option updates aren't a concern then singleton is the obvious choice. If not, you're basically tied to the lifetime of the object that consumes the option.
In the long run I think we'll need to provide an easy extension to allow users of a particular option type to say that they also want to be able to consume it directly without the IOptions<T> wrapper.
Reacted by David Pine, Günther Foidl, David Nelson, Giorgi Chakhidze, Ole Xandr, Wouter Paap, Enrique Sierra Gutiérrez, Johan Book, Mary Gao, Emil Wall and 6 more
@IEvangelist understood that this was a decision made by the product team at the time they designed the pattern. All patterns involve tradeoffs, or pros/cons. When documenting a pattern, I want to understand what tradeoffs the product team made, so that I can determine whether they match my situation, and therefore whether I should follow this pattern or not. But in the list of options interfaces, the first entry only says what IOptions does not do, not what it does do.
With an interface, extension methods can be written as helpers
I'm not clear on how the presence of an interface makes writing such an extension method any easier, or more valuable.
Wrapping the configurations in interfaces allows for reloading of the underlying data-bound settings instances
This is the only advantage I can think of, and is a good example of the tradeoffs I am talking about. If reloading is the reason why IOptions exists, and I have no need to change the configuration dynamically, then I don't need the interface.
(Also, if that's the reason why IOptions exists then the examples should probably not show reading the values one time in the constructor and storing them as fields, since that would defeat the point of the pattern.)
@davidfowl Thank you, I think that answers my question (and mostly matches up with what I expected, but I wanted to make sure I wasn't missing anything). I definitely think it is worth putting this explanation in the docs.
The docs talk a lot about IOptionsSnapshot and IOptionsMonitor, and comparing the capabilities of the two; but very little is mentioned about the behavior of IOptions itself, even though that is what most examples in the wild use. Making it clear that IOptions defers evaluation (and does not cache, or invalidates the cache on config change?), which enables updating the config while the app is running, would help. Most discussion of dynamically updating config talks about IOptionsMonitor, which makes it seem like IOptions does not support that.
Why should we use IOptions when we can inject the configuration object directly?
This is the easy question. Objects are nicer to deal with than strings and converting those strings to various data types in C#.
I didn't mean "inject the Configuration object directly", I meant "inject my strongly-typed configuration object directly" (as demonstrated in my Java example).
Using a generic wrapper type gives us the ability to decouple the lifetime of the option from the DI container.
The doc claims that IOptions<T> "Is registered as a Singleton". I understand you to be saying that the IOptions<T> instance is a singleton, but because of deferred evaluation, the lifetime of the underlying T is not necessarily a singleton. That might be worth clarifying in the docs.
The other advantage using the wrapper type has is it means you don't need to register the wrapped type
You don't need to register T as a "service", but you do have to register it with its configuration section, which to most people (including me) seems to amount to the same thing. Beyond needing to choose a lifetime, is there a reason why I should care about the distinction?
All patterns involve tradeoffs, or pros/cons. When documenting a pattern, I want to understand what tradeoffs the product team made, so that I can determine whether they match my situation, and therefore whether I should follow this pattern or not.
I agree, all patterns involve tradeoffs, or pros/cons. This pattern is just another tool, do with it what you will. You're free to not follow the pattern. I felt as though it was more important to document "the how" rather than "the why", less of a conceptual article and more of a guide. I really do appreciate this feedback.
With an interface, extension methods can be written as helpers
I'm not clear on how the presence of an interface makes writing such an extension method any easier, or more valuable.
My thought on extensibility was a bit preemptive, and just an initial thought. With generics in C#, you could leverage generic type parameters for an extension method, otherwise you'd end up extending each concrete class or worse, object which isn't great. When you have an interface it serves as a wrapper around the configuration object representing the settings. It is easier to write extension methods as you can extend the interface. This allows for other tools to rely on this abstraction, and they're free to extend it.
Without an interface wrapping the TOptions of IOptions<TOptions>.Value, you'd have to individually add extension methods to each TOptions or something like extending object.
I suppose I can see either way, it's still a valid question -- "why is it needed?".
Wrapping the configurations in interfaces allows for reloading of the underlying data-bound settings instances
This is the only advantage I can think of, and is a good example of the tradeoffs I am talking about. If reloading is the reason why IOptions exists, and I have no need to change the configuration dynamically, then I don't need the interface.
There is a call attention regarding how "IOptions<TOptions> is registered as a Singleton and can be injected into any service lifetime.". This means, for every TOptions reference type registered in the app's DI container, we will have one and only one instance for that type pair. Some point in the future, when IOptions<TOptions> is resolved there will only be a single instance of the corresponding type parameter / ref type. The lifetime of that instance of IOptions<SomeOptions>, so if you have six concrete configuration objects you'd have six instances of this singleton, varied by type parameter.
I do have a PR to address this issue, it adds a section on the rationale. Does this make sense to you, would you want to change anything? See, additional lines here
Making it clear that IOptions defers evaluation (and does not cache, or invalidates the cache on config change?), which enables updating the config while the app is running, would help. Most discussion of dynamically updating config talks about IOptionsMonitor, which makes it seem like IOptions does not support that.
IOptions defers evaluation until .Value is accessed and is cached.
The doc claims that IOptions "Is registered as a Singleton". I understand you to be saying that the IOptions instance is a singleton, but because of deferred evaluation, the lifetime of the underlying T is not necessarily a singleton. That might be worth clarifying in the docs.
That's right, but T is a cached in this case so it's effectively a lazy singleton.
You don't need to register T as a "service", but you do have to register it with its configuration section, which to most people (including me) seems to amount to the same thing. Beyond needing to choose a lifetime, is there a reason why I should care about the distinction?
The other advantage using the wrapper type has is it means you don't need to register the wrapped type
You don't need to register T as a "service", but you do have to register it with its configuration section, which to most people (including me) seems to amount to the same thing. Beyond needing to choose a lifetime, is there a reason why I should care about the distinction?
Can you clarify?
@davidfowl if I may, I believe the point @eatdrinksleepcode was trying to make has merit: The statement "you don't need to register the wrapped type", while true, makes it sound like the benefit is that consumers can avoid application startup code for that type. This is not the case because on startup we need at least two things: DI registration, and binding to configuration, and the latter is still needed.
To make it more concrete with code:
While there is indeed no need to register the type with DI: builder.Services.AddSingleton<MyOptions>()
There is still a need to bind the type to configuration: builder.Services.Configure<MyOptions>(builder.Configuration.GetSection(MyOptions.MySectionName))
So per each type there is still boilerplate startup code needed for the options consumer, roughly in the same place in the execution pipeline. So as an entry-point developer, you don't really need to "do less", or "know about less", if that makes sense to you.
I see that the docs already recommend as best practice to create a constant field with the configuration section name. I can imagine that IOptions<T> is purposely designed to be abstracted away from configuration concerns, which led to the .NET team reach the conclusion that TOptions should be allowed to remain a POCO, and not be required to implement any particular interface. This is a powerful abstraction, but it has a semantic drawback and an implementation drawback:
Semantically - it seems to clash with the recommended guideline to expose a const meta-field on the TOptions type. Yes, it keeps the options type abstracted away from configuration, but it still states "I will likely need to be identified by name" without providing a structured way to communicate with this need.
Concretely - it loses the ability for library providers to express something like "If my options need to be referenced by name (which is the standard case when reading them from configuration), let the entry point reference them by default using [this name]." An entry point can then translate this statement to "Since I use configuration to provide options, please take options by default using their [this name] as a configuration section". If these semantics are expressed by the framework, then ASP.NET Core itself could consume them to provide said default configuration for all such options. I have no idea about the relevant statistics, but I can only imagine that the vast majority of implementations out there could benefit from such an expression, relieving them from the need to code startup logic for each and every options type used by any library in their library tree. And in a very real sense, it could reduce cognitive load on application developers: There are multiple ways to inject, configure, register in DI and bind options to configuration. Only for the latter, which is the topic here, there are at least the following options I could find: ConfigurationBinder.Bind(), ConfigurationBinder.Get<T>() (binds and returns), IServiceCollection.Configure<T>() (extension), OptionsBuilder<T>.Configure(), OptionsBuilder<T>.Bind() (extension, mentioned bad practice here, and yet documented in multiple examples in main docs here and here - @IEvangelist is this worth looking into?), OptionsBuilder<T>.BindConfiguration() (extension). As a software architect and a platform developer in my company, I'm being asked "what is our company's best practice?". If default configuration binding worked out of the box, application developers would need to spend less efforts on learning this rich world of options (no pun intended).
Does this make sense so far, or does the .NET team have a different view on whether this is a valuable benefit for application writers?
Is there already a built-in default binding in ASP.NET Core that I am overlooking?
If there is no such default binding, would the team consider exploring introducing it?
Of course we could work around this ourselves, either by exposing an interface or by auto-configuring some default naming convention (e.g. options class name), but those are much less powerful as they would:
require maintenance and own documentation
still have to be provided as a package for each application developer to use (can be alleviated by injecting it into our base template, but it still ends up as code within every web application)
an interface will only work for our own option types, a naming convention could work but is the sort of non-declarative solution we try to avoid
would still have to figure out how to elegantly bind at once all types available at runtime (preferably without reflection), as binding an interface doesn't seem to work
===
Thinking in solutions - a potential implementation could involve a stricter variation of the IOptions interface (probably inheriting from it), which forces TOptions to implement some interface exposing this value. I'd opt to name it INamedOptions but that seems to collide with the concept of named options (or maybe it doesn't?). Either way, it could look something like this:
This would allow configuring all such options in a single bind by introducing a new .Configure<T>() variant accepting a delegate Func<T, IConfiguration> configSectionGetter, and the call site could look something like this: builder.Services.Configure<INamedOptions>(x => builder.Configuration.GetSection(x.OptionsName));
I don't know how difficult the implementation is. A complexity is that so far configuring only seems to work per concrete type, meaning that configuring INamedOptions and injecting MyOptions does not currently work (at least using the configuration APIs I've tried, perhaps one of the others already caters to this need?).
If this is feasible, then this should allow eliminating any and all default startup code per each options type. If more control is explicitly needed, defaults can always be overridden by an explicit call to any of the aforementioned binding APIs.
One downside I can detect is that unlike a type-level constant, this would make the options name a part of the instance. I suppose this is pretty much why it isn't there today. I don't like it very much but can't see how to avoid it in this implementation since there is no type-level inheritance in C#. I'm wondering if it is a dealbreaker from framework perspective. A variant can be exposing a method instead of a property, to at least alleviate serialization concerns.
There are probably many other implementations possible.
Is any of this relatable? Is this worth opening a new feature request for so it can be further explored?
Apologies for the length of the post, wanted to lay my thoughts in one go.
I think want you want to do is possible today by plugging into the existing infrastructure. It’s not something we’d recommend globally for all options.
Why not build a package that enables the developer experience you want for your types (or see if one already exists on nuget)?
I think want you want to do is possible today by plugging into the existing infrastructure.
Can you elaborate? Perhaps give a code example?
It’s not something we’d recommend globally for all options.
Can you specify why? Any particular part in the reasoning presented above does not align with the .Net team's vision? Do you agree with some of the thoughts regarding more expressive semantics and reduced cognitive load?
Why not build a package that enables the developer experience you want for your types (or see if one already exists on nuget)?
I'm not referring to our own types, I'm talking about the ability to auto-bind all option types by default, including framework and third-party types. Is there a good reason why it should be preferable to code a name for each one separately, and repeatedly in each application, when the defaults could work just fine in the vast majority of cases? Option types already define their schema's contract, why not let them prefer their default container name as well, if it removes the need for every application to code that binding one by one?
Reacted by Dmytro Pryvedeniuk, Max Kostovetski and Steve "Ardalis" Smith
Then I suspect you could probably do it by registering custom implementations of the options interfaces (IOptions<>, IOptionsMonitor<>, etc.) that either delegate to the built-in implementations (for non-IAutoConfiguredOptions) or automatically bind to DefaultConfigurationSection and then delegate to the built-in implementations (if T is IAutoConfiguredOptions).
Activity
IEvangelist commentedon Feb 18, 2021
Hi 👋 @eatdrinksleepcode
Thank you so much for posting this issue. We really appreciate it. That is a fair question to ask, however it is more of a product team question than it is a docs question. I suppose the docs could add this information, assuming that level of detail was readily available.
This was an architectural and API decision that would have been made a long time ago now. I believe that it is part of a larger pattern, where interfaces are preferred for dependency injection over concrete classes. The options pattern was specifically designed to function with a set of well defined interfaces:
https://docs.microsoft.com/dotnet/core/extensions/options#options-interfaces
A few immediate thoughts/benefits come to mind when comparing the decision to use an interface:
With an interface, extension methods can be written as helpers (not a create extension method, but you get the point)
Wrapping the configurations in interfaces allows for reloading of the underlying data-bound settings instances, I'm not sure how that would work otherwise.
Does any of this help? /cc @davidfowl
davidfowl commentedon Feb 18, 2021
This is the easy question. Objects are nicer to deal with than strings and converting those strings to various data types in C#. You can divorce the consumption of your poco from how that poco is created. I think it's fine to start by grabbing configuration and readings things from it but ideally as your project grows in complexity, you'd want to move all of the code that goes from IConfiguration -> poco in a single place.
This comes up ALOT so I'm happy to provide something in the docs so we at least have something to point to. Using a generic wrapper type gives us the ability to decouple the lifetime of the option from the DI container. That has a couple of uses:
Toption from various places and chose the lifetime semantics without changing anything aboutT.IOptions<T>deferring resolution until the Value property is accessed.IOptionsSnapshot<T>to cache the most recent value of T within a scoped lifetimeIOptionsMonitor<T>to resolve the most recent value of T whenever it changes (due to a configuration change)Tin this case). We do this in a bunch of places where we have services consumeIOptions<T>without explicit registration ofTand consumption works. This is convenient if you're authoring a library with simple defaults where you don't want to force the caller to register options into the DI container with a specific lifetime.Tisn't doesn't need to be modified to describe things about it. That's what the wrapper type is for.Now
IOptions<T>vsT? If you choose to injectTdirectly into your types directly:In the long run I think we'll need to provide an easy extension to allow users of a particular option type to say that they also want to be able to consume it directly without the
IOptions<T>wrapper.Added rationale section per dotnet#22886
eatdrinksleepcode commentedon Feb 24, 2021
@IEvangelist understood that this was a decision made by the product team at the time they designed the pattern. All patterns involve tradeoffs, or pros/cons. When documenting a pattern, I want to understand what tradeoffs the product team made, so that I can determine whether they match my situation, and therefore whether I should follow this pattern or not. But in the list of options interfaces, the first entry only says what
IOptionsdoes not do, not what it does do.I'm not clear on how the presence of an interface makes writing such an extension method any easier, or more valuable.
This is the only advantage I can think of, and is a good example of the tradeoffs I am talking about. If reloading is the reason why
IOptionsexists, and I have no need to change the configuration dynamically, then I don't need the interface.(Also, if that's the reason why
IOptionsexists then the examples should probably not show reading the values one time in the constructor and storing them as fields, since that would defeat the point of the pattern.)eatdrinksleepcode commentedon Feb 24, 2021
@davidfowl Thank you, I think that answers my question (and mostly matches up with what I expected, but I wanted to make sure I wasn't missing anything). I definitely think it is worth putting this explanation in the docs.
The docs talk a lot about
IOptionsSnapshotandIOptionsMonitor, and comparing the capabilities of the two; but very little is mentioned about the behavior ofIOptionsitself, even though that is what most examples in the wild use. Making it clear thatIOptionsdefers evaluation (and does not cache, or invalidates the cache on config change?), which enables updating the config while the app is running, would help. Most discussion of dynamically updating config talks aboutIOptionsMonitor, which makes it seem likeIOptionsdoes not support that.I didn't mean "inject the
Configurationobject directly", I meant "inject my strongly-typed configuration object directly" (as demonstrated in my Java example).The doc claims that
IOptions<T>"Is registered as a Singleton". I understand you to be saying that theIOptions<T>instance is a singleton, but because of deferred evaluation, the lifetime of the underlyingTis not necessarily a singleton. That might be worth clarifying in the docs.You don't need to register
Tas a "service", but you do have to register it with its configuration section, which to most people (including me) seems to amount to the same thing. Beyond needing to choose a lifetime, is there a reason why I should care about the distinction?IEvangelist commentedon Feb 25, 2021
I agree, all patterns involve tradeoffs, or pros/cons. This pattern is just another tool, do with it what you will. You're free to not follow the pattern. I felt as though it was more important to document "the how" rather than "the why", less of a conceptual article and more of a guide. I really do appreciate this feedback.
My thought on extensibility was a bit preemptive, and just an initial thought. With generics in C#, you could leverage generic type parameters for an extension method, otherwise you'd end up extending each concrete class or worse,
objectwhich isn't great. When you have an interface it serves as a wrapper around the configuration object representing the settings. It is easier to write extension methods as you can extend the interface. This allows for other tools to rely on this abstraction, and they're free to extend it.Without an interface wrapping the
TOptionsofIOptions<TOptions>.Value, you'd have to individually add extension methods to eachTOptionsor something like extendingobject.I suppose I can see either way, it's still a valid question -- "why is it needed?".
There is a call attention regarding how "
IOptions<TOptions>is registered as a Singleton and can be injected into any service lifetime.". This means, for everyTOptionsreference type registered in the app's DI container, we will have one and only one instance for that type pair. Some point in the future, whenIOptions<TOptions>is resolved there will only be a single instance of the corresponding type parameter / ref type. The lifetime of that instance ofIOptions<SomeOptions>, so if you have six concrete configuration objects you'd have six instances of this singleton, varied by type parameter.I do have a PR to address this issue, it adds a section on the rationale. Does this make sense to you, would you want to change anything? See, additional lines here
davidfowl commentedon Feb 25, 2021
IOptions defers evaluation until
.Valueis accessed and is cached.That's right, but T is a cached in this case so it's effectively a lazy singleton.
Can you clarify?
Added rationale section per #22886 (#22907)
Devops ebook: Actions index (#23462)
Releases - DevOps for ASP.NET Core Developers eBook V1.1 (#23784)
tsemer commentedon Jan 17, 2024
@davidfowl if I may, I believe the point @eatdrinksleepcode was trying to make has merit: The statement "you don't need to register the wrapped type", while true, makes it sound like the benefit is that consumers can avoid application startup code for that type. This is not the case because on startup we need at least two things: DI registration, and binding to configuration, and the latter is still needed.
To make it more concrete with code:
builder.Services.AddSingleton<MyOptions>()builder.Services.Configure<MyOptions>(builder.Configuration.GetSection(MyOptions.MySectionName))So per each type there is still boilerplate startup code needed for the options consumer, roughly in the same place in the execution pipeline. So as an entry-point developer, you don't really need to "do less", or "know about less", if that makes sense to you.
I see that the docs already recommend as best practice to create a constant field with the configuration section name. I can imagine that
IOptions<T>is purposely designed to be abstracted away from configuration concerns, which led to the .NET team reach the conclusion thatTOptionsshould be allowed to remain a POCO, and not be required to implement any particular interface. This is a powerful abstraction, but it has a semantic drawback and an implementation drawback:TOptionstype. Yes, it keeps the options type abstracted away from configuration, but it still states "I will likely need to be identified by name" without providing a structured way to communicate with this need.ConfigurationBinder.Bind(),ConfigurationBinder.Get<T>()(binds and returns),IServiceCollection.Configure<T>()(extension),OptionsBuilder<T>.Configure(),OptionsBuilder<T>.Bind()(extension, mentioned bad practice here, and yet documented in multiple examples in main docs here and here - @IEvangelist is this worth looking into?),OptionsBuilder<T>.BindConfiguration()(extension). As a software architect and a platform developer in my company, I'm being asked "what is our company's best practice?". If default configuration binding worked out of the box, application developers would need to spend less efforts on learning this rich world of options (no pun intended).Does this make sense so far, or does the .NET team have a different view on whether this is a valuable benefit for application writers?
Is there already a built-in default binding in ASP.NET Core that I am overlooking?
If there is no such default binding, would the team consider exploring introducing it?
Of course we could work around this ourselves, either by exposing an interface or by auto-configuring some default naming convention (e.g. options class name), but those are much less powerful as they would:
===
Thinking in solutions - a potential implementation could involve a stricter variation of the
IOptionsinterface (probably inheriting from it), which forcesTOptionsto implement some interface exposing this value. I'd opt to name itINamedOptionsbut that seems to collide with the concept of named options (or maybe it doesn't?). Either way, it could look something like this:This would allow configuring all such options in a single bind by introducing a new
.Configure<T>()variant accepting a delegateFunc<T, IConfiguration> configSectionGetter, and the call site could look something like this:builder.Services.Configure<INamedOptions>(x => builder.Configuration.GetSection(x.OptionsName));I don't know how difficult the implementation is. A complexity is that so far configuring only seems to work per concrete type, meaning that configuring
INamedOptionsand injectingMyOptionsdoes not currently work (at least using the configuration APIs I've tried, perhaps one of the others already caters to this need?).If this is feasible, then this should allow eliminating any and all default startup code per each options type. If more control is explicitly needed, defaults can always be overridden by an explicit call to any of the aforementioned binding APIs.
One downside I can detect is that unlike a type-level constant, this would make the options name a part of the instance. I suppose this is pretty much why it isn't there today. I don't like it very much but can't see how to avoid it in this implementation since there is no type-level inheritance in C#. I'm wondering if it is a dealbreaker from framework perspective. A variant can be exposing a method instead of a property, to at least alleviate serialization concerns.
There are probably many other implementations possible.
Is any of this relatable? Is this worth opening a new feature request for so it can be further explored?
Apologies for the length of the post, wanted to lay my thoughts in one go.
davidfowl commentedon Jan 17, 2024
I think want you want to do is possible today by plugging into the existing infrastructure. It’s not something we’d recommend globally for all options.
Why not build a package that enables the developer experience you want for your types (or see if one already exists on nuget)?
tsemer commentedon Jan 18, 2024
Thanks for your swift response.
Can you elaborate? Perhaps give a code example?
Can you specify why? Any particular part in the reasoning presented above does not align with the .Net team's vision? Do you agree with some of the thoughts regarding more expressive semantics and reduced cognitive load?
I'm not referring to our own types, I'm talking about the ability to auto-bind all option types by default, including framework and third-party types. Is there a good reason why it should be preferable to code a name for each one separately, and repeatedly in each application, when the defaults could work just fine in the vast majority of cases? Option types already define their schema's contract, why not let them prefer their default container name as well, if it removes the need for every application to code that binding one by one?
sgbench commentedon Oct 16, 2025
Something like this might suffice for your own options types:
If you want an experience like this:
Then I suspect you could probably do it by registering custom implementations of the options interfaces (
IOptions<>,IOptionsMonitor<>, etc.) that either delegate to the built-in implementations (for non-IAutoConfiguredOptions) or automatically bind toDefaultConfigurationSectionand then delegate to the built-in implementations (ifTisIAutoConfiguredOptions).