Skip to content
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

Configuration Binding source generator throws InvalidOperationException at runtime for type with custom [TypeConverter] use within a dictionary #93335

Open
martincostello opened this issue Oct 11, 2023 · 3 comments
Labels
area-Extensions-Configuration source-generator Indicates an issue with a source generator feature
Milestone

Comments

@martincostello
Copy link
Member

Description

If the configuration binding source generator is used with a type that uses a custom [TypeConverter] to parse the type from a string, the generated code will throw an InvalidOperationException at runtime because properties are not present in the configuration that map to the object's properties when the value is a value in a dictionary.

For example, if this type is used for this configuration object in conjunction with a JSON configuration such as:

{
  "Site": {
    "Keyed": {
      "Somewhere": "0,0"
    }
  }
}

The same issue does not seem to occur if the object is not within a dictionary. I have not tested the behavior when the object is a member of an array.

Reproduction Steps

  1. Clone martincostello/config-binding-source-generator-typeconverter-repro
  2. Run dotnet run --project .\Repro\

Expected behavior

The configuration binding source generator produces code that is able to use the type converter to produce an instance of the given type from the string Value property of the IConfiguration passed to the generated method.

Actual behavior

An InvalidOperationException is thrown the first time the application attempts to retrieve the bound configuration object.

Unhandled exception. System.InvalidOperationException: Cannot create instance of type 'Repro.Geolocation' because parameter 'latitude' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.InitializeGeolocation(IConfiguration configuration, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 174
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, IDictionary`2& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 134
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, SiteOptions& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 160
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCoreMain(IConfiguration configuration, Object instance, Type type, Action`1 configureOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 83
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.<>c__DisplayClass1_0`1.<Configure>b__0(TOptions instance) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 58
   at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
   at Microsoft.Extensions.Options.UnnamedOptionsManager`1.get_Value()
   at Program.<Main>$(String[] args) in .\config-binding-source-generator-typeconverter-repro\Repro\Program.cs:line 11

Regression?

Unknown compared to the previews and rc.1 - I had the configuration binding source generator disabled before RC2 due to #90851, #90987 and #91258 that prevented my code from running this far.

Known Workarounds

Disable the configuration binding source generator or refactor code to not use a custom [TypeConverter].

Configuration

.NET SDK 8.0.100-rc.2.23502.2

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Oct 11, 2023
@ghost
Copy link

ghost commented Oct 11, 2023

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

Issue Details

Description

If the configuration binding source generator is used with a type that uses a custom [TypeConverter] to parse the type from a string, the generated code will throw an InvalidOperationException at runtime because properties are not present in the configuration that map to the object's properties when the value is a value in a dictionary.

For example, if this type is used for this configuration object in conjunction with a JSON configuration such as:

{
  "Site": {
    "Keyed": {
      "Somewhere": "0,0"
    }
  }
}

The same issue does not seem to occur if the object is not within a dictionary. I have not tested the behavior when the object is a member of an array.

Reproduction Steps

  1. Clone martincostello/config-binding-source-generator-typeconverter-repro
  2. Run dotnet run --project .\Repro\

Expected behavior

The configuration binding source generator produces code that is able to use the type converter to produce an instance of the given type from the string Value property of the IConfiguration passed to the generated method.

Actual behavior

An InvalidOperationException is thrown the first time the application attempts to retrieve the bound configuration object.

Unhandled exception. System.InvalidOperationException: Cannot create instance of type 'Repro.Geolocation' because parameter 'latitude' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.InitializeGeolocation(IConfiguration configuration, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 174
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, IDictionary`2& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 134
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, SiteOptions& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 160
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCoreMain(IConfiguration configuration, Object instance, Type type, Action`1 configureOptions) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 83
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.<>c__DisplayClass1_0`1.<Configure>b__0(TOptions instance) in .\config-binding-source-generator-typeconverter-repro\Repro\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 58
   at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
   at Microsoft.Extensions.Options.UnnamedOptionsManager`1.get_Value()
   at Program.<Main>$(String[] args) in .\config-binding-source-generator-typeconverter-repro\Repro\Program.cs:line 11

Regression?

Unknown compared to the previews and rc.1 - I had the configuration binding source generator disabled before RC2 due to #90851, #90987 and #91258 that prevented my code from running this far.

Known Workarounds

Disable the configuration binding source generator or refactor code to not use a custom [TypeConverter].

Configuration

.NET SDK 8.0.100-rc.2.23502.2

Other information

No response

Author: martincostello
Assignees: -
Labels:

area-Extensions-Configuration

Milestone: -

@tarekgh tarekgh added this to the 9.0.0 milestone Oct 11, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Oct 11, 2023
@tarekgh tarekgh added untriaged New issue has not been triaged by the area owner source-generator Indicates an issue with a source generator feature labels Oct 11, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Oct 11, 2023
@ericstj
Copy link
Member

ericstj commented Oct 11, 2023

This was intentionally out of scope for v1 of the configuration source generator. Please see #83599 for the way we're thinking about handling this as well as suggested workarounds.

@martincostello
Copy link
Member Author

Fair enough - I did think it weird though that it doesn't work in a dictionary, but it seemed to work outside of one.

However, I just tweaked my code a bit and had another look and in the case of it not being in a dictionary, it instead just silently doesn't bind anything at all and you get left with the default value, rather than an exception being thrown.

Repro/appsettings.json

-    "Keyed": {
-      "Somewhere": "0,0"
-    }
+    "Single": "1,2"

Repro/Program.cs

- _ = app.Services.GetRequiredService<IOptions<SiteOptions>>().Value;
+ var opts = app.Services.GetRequiredService<IOptions<SiteOptions>>().Value;
+ Console.WriteLine(opts.Single);

Output:

0,0

@ericstj ericstj modified the milestones: 9.0.0, 10.0.0 Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Extensions-Configuration source-generator Indicates an issue with a source generator feature
Projects
None yet
Development

No branches or pull requests

3 participants