-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extensibility for Configuration Binding Source Generator #83599
Comments
Tagging subscribers to this area: @dotnet/area-extensions-configuration Issue DetailsInitial discussion - #44493 (comment) cc @eerhardt @ericstj @davidfowl
|
From @eerhardt in #83533 (comment):
|
This makes sense to me. Just that it doesn't account for converters registered at runtime i.e. with |
The "registered at runtime" may be interesting to a subset of customers. But the way to enable that would mean all the other customers would have to pay for it. My preference would be to only enable it if we get feedback it is required. And if we do, we make it opt-in, somehow. |
I don't feel great about making any bets on the TypeConverter infrastructure. It's pretty old and never designed with AOT and Link-ability in mind.
This wasn't previously honored by ConfigurationBinder. It only looked up the converter for the type. runtime/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs Line 855 in 6ef9d10
While this would provide a way for folks to extend the system and be consistent with what the binder used to do, it might still be too heavy. Converters handle more than just String->T and the linker might not be able to shake out a small enough code-path to make this viable. An alternative would be to explicitly design some hook for folks to register a lightweight Func<string,T> conversion that we could honor at runtime. Heck - if they wanted to, they could even decide to add back in converter that's driven off the TypeConverter infrastructure. |
Given all these considerations, this whole scenario seems like something we should await customer/stakeholder feedback for before implementing a solution. |
cc @geeknoid. I believe you have used this functionality in the past. Would not supporting customized converting of |
Correct. But this has been an ask from customers. See my "see also" above: #36545. |
We're asking about existing types we don't own right? Why wouldn't we support |
We only use type converters for this purpose, ie. configuration binding. As mentioned in the other issue, A mechanism to provide a We could always create a |
@pinkfloydx33 AFAIK the goal for the generator in .NET 8 is parity (to the degree possible) with the reflection implementation. Thus On a related note If feedback indicates that there are crucial dependences on |
Probably you don't need to have the IParseable interface to permit types to define their own bind logic. We could probe IParseable first, then for the method signature that would have been there if it had implemented IParseable (to support frameworks that don't have IParseable). I think the scenario of folks defining bind logic for types they own (maybe IParseable), and folks defining bind logic for types they don't own are legitimate use cases and things the reflection-based binder supported. We need to do it in a different way than TypeConverter since that has a large code size and uses a significant amount of refection. |
This is what minimal APIs does.
Agreed. There are also language features that will help here (implicit extensions). |
Triage: given we're in the later stages of the .NET 8 dev time, we've decided to pause on implementing an extensibility feature for the source generator in .NET 8. Effectively, the generator won't add code to check for We don't have a strong signal that these features are needed right now, but we'll be following feedback here closely and can revisit the decision. |
@pinkfloydx33 I noticed your down-vote. Would you like to share your scenario with us to see if we're missing something? Do you have an application that uses custom bind logic today? |
I believe I shared it above and in the linked issue. Particularly we have a bunch of settings that we pass in as a delimited list rather than an actual array of values. We then use a type converter to convert this to a HashSet of a specific type. We've gone this route in some cases as we've found it easier to manage layered settings if we are completely overwriting them, versus trying to layer array indexed positions which have non-obvious semantics. Say we have a Array semantics in the overlays can be confusing to a non-dev. They also must make sense of the array index syntax in general. So instead, we require them to specify the entire desired value as a comma-delimited string: Without support for type converters (or IParseable along with our own implementing HashSet subclass) the source gen'd binder is worthless to us and we'll have to stick with the reflection-based binder. |
Could you do the same thing with a poco that had a string value and an accessor that lazily initialized the HashSet from the string? using Microsoft.Extensions.Configuration;
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string,string?>
{
["MyOptions:Value"] = "2,3"
}).Build();
var converter = config.GetSection("MyOptions").Get<MyHashSetConverter>()!;
Console.WriteLine(converter.Converted.Count);
public class MyHashSetConverter
{
private HashSet<int>? _converted;
public string? Value { get; set; }
public HashSet<int> Converted { get => _converted ??= Parse(Value!); }
private HashSet<int> Parse(string value)
{
var parts = value.Split(',');
return new HashSet<int>(parts.Select(x => int.Parse(x)));
}
} |
@ericstj Are we out of time to implement |
@ericstj I hadn't considered doing that; it should/could work. It's feels a little unnecessary but is definitely a valid workaround that would work with source gen. Thanks! However, I believe that another scenario I have won't work. We bind to I suppose we can use a similar trick and cache the parsed address/network object. I'm not crazy about that--less so than the case cited above. This would be covered with Just my 2cp. We've got the work around demonstrated above so I'm not bound to reflection... though I may just wait until it's supported before trying to switch. |
We discussed with @tarekgh, @eerhardt, and @layomia last week. The cut-off for feature work for 8.0 is effectively today. We don't have a strong signal that folks are blocked on this extensibility, and we didn't want to rush something. We can always add this later, but it's much more difficult to remove it if we got it wrong. |
I'm the author of #89254 which proposed a mechanism to control binding of types not owned by the application (In my case The API described in the opening of this PR would solve my case perfectly. namespace Microsoft.Extensions.Configuration
{
public class BinderOptions
{
public bool BindNonPublicProperties { get; set; }
public bool ErrorOnUnknownConfiguration { get; set; }
// New
// Note: last one wins, just like with current `TypeConverter` look up behavior.
// Note: boxing for custom structs. Should be okay since binding generally isn't in hot path.
public IDictionary<Type, Func<string, object>> Converters { get; }
}
} Is there any chance to get just that into .NET 8? That ignores the source generator AOT problem but is still an improvement in customization for configuration binding. It seems relatively simple to implement, we just need to thread the options object down through the binder to where it's currently finding the That doesn't feel like it adds a lot of risk that we've gotten something wrong. A generic override seems compatible with a later, smarter way to handle |
@christopherbahr right now we're focused on getting the quality of the release right. Better not to rush this. Often times the devil is in the details with features like this -- e.g., we need to get the order of precedence right; would folks need other inputs such as |
@layomia Fair enough, I'm sure you understand the constraints and concerns much better than I do. It sounds like a couple weeks ago you were looking for some signal that people were blocked on this sort of extensibility. It sounds like we're not going to make .NET 8 but put me down as part of that signal for the next release. |
@adamsitnik had a scenario described in #91324 where he binds to an abstract type and data helps discriminate which derived type to create. |
We are too late in 9.0 to be adding new extensibility features to Configuration. I do want us to reconsider this in 10.0. Would be interested to hear from others like @eerhardt @tarekgh @davidfowl what is most valuable to tackle here. |
AOT support would be very much appreciated for something like this. |
Initial discussion - #44493 (comment)
cc @eerhardt @ericstj @davidfowl
Extensibility for Configuration Binding Source Generator
Background
The configuration binding source generator provides AOT and trim-friendly configuration in ASP.NET Core. It is an alternative to the pre-exising reflection-based implementation. For each type to bind, the reflection implementation checks whether a
TypeConverter
instance exists and uses it if so. This was mainly applicable as a convenient abstraction to bind to built-in primitives such asint
andstring
which are parsable from string. However, theTypeConverter
mechanism is not friendly for AOT usage. As a result, the binding generator does not look upTypeConverter
usage.TypeConverter
inadvertently provided a way for developers to convert binding behavior. We've seen partner teams use it to parse non-primitive types such as security certificates. This begs the question of whether we do need to support it, or provide a replacement mechanism to cover customization scenarios.Proposed customization strategies
We don't want to honor
TypeConverter
in the generator implementation. References to it would largely undo the major benefit of the generator, which is AOT and linking friendliness. Since the generator is new, we have an opportunity to provide a better customization experience.Check and honor
IParsable<T>
implementations.This only works for design-time customization. Does not work for non-owned types.
API proposal: new API for runtime configuration
We would add a new converters dictionary to register converter instances. This would be last one wins.
Customization restrictions
IParsable<T>
, we would only support types directly parsable from string.TypeConverter
supports & the generator now handles with hand-written logic.Order of customization preference
IParsable<T>
implementation if detected (replacing generators handwritten logic for intrinsic types).The text was updated successfully, but these errors were encountered: