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

ConfigurationBinder source generator throws on an abstract member rather than binding to existing value #92137

Closed
ericstj opened this issue Sep 15, 2023 · 3 comments · Fixed by #93090

Comments

@ericstj
Copy link
Member

ericstj commented Sep 15, 2023

Description

When encountering an abstract type in an object graph the configuration generator tries to create it and throws.

This is inconsistent with the runtime binder, which will use an existing instance.

I wonder if we should warn on this or not. Definitely if the abstract type is the root and we're generating Get calls that's a problem. Anywhere else it could be supported if the member is already initialized - since we won't need to create one in the binder.

Reproduction Steps

Run attached repro project:
bindAbstract.zip

Also shown here: https://gist.github.com/ericstj/121fe52f9a1b5115b246c08605c685c0

To see expected behavior remove EnableConfigurationBindingGenerator. You'll see the output

options.Abstract.GetHashCode() before bind: 55915408
options.Abstract.GetHashCode() after bind: 55915408
John Doe

Note that the runtime binder didn't rely on seeing the derived type and creating a new one -- this is apparent by seeing that the object is the same before and after then bind.

Expected behavior

Repro runs successfully and sets the members on the existing instance of the derived type. Not creating a new instance nor throwing.

Actual behavior

Warning emitted by the source generator:

C:\scratch\bindAbstract\Program.cs(11,1): warning SYSLIB1100: Cannot create instance of type 'MyAbstractClass' because
it is missing a public instance constructor. (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib
1100.md)

And exception thrown at runtime:

Unhandled exception. System.InvalidOperationException: Cannot create instance of type '<global namespace>.MyAbstractClass' because it is missing a public instance constructor.
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, MyOptions& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in C:\scratch\bindAbstract\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 144
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.Bind_MyOptions(IConfiguration configuration, Object instance) in C:\scratch\bindAbstract\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 48
   at Program.<Main>$(String[] args) in C:\scratch\bindAbstract\Program.cs:line 11

Regression?

No response

Known Workarounds

Disable generator.

Configuration

No response

Other information

No response

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

ghost commented Sep 15, 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

When encountering an abstract type in an object graph the configuration generator tries to create it and throws.

This is inconsistent with the runtime binder, which will use an existing instance.

I wonder if we should warn on this or not. Definitely if the abstract type is the root and we're generating Get calls that's a problem. Anywhere else it could be supported if the member is already initialized - since we won't need to create one in the binder.

Reproduction Steps

Run attached repro project:
bindAbstract.zip

Also shown here: https://gist.github.com/ericstj/121fe52f9a1b5115b246c08605c685c0

To see expected behavior remove EnableConfigurationBindingGenerator. You'll see the output

options.Abstract.GetHashCode() before bind: 55915408
options.Abstract.GetHashCode() after bind: 55915408
John Doe

Note that the runtime binder didn't rely on seeing the derived type and creating a new one -- this is apparent by seeing that the object is the same before and after then bind.

Expected behavior

Repro runs successfully and sets the members on the existing instance of the derived type. Not creating a new instance nor throwing.

Actual behavior

Warning emitted by the source generator:

C:\scratch\bindAbstract\Program.cs(11,1): warning SYSLIB1100: Cannot create instance of type 'MyAbstractClass' because
it is missing a public instance constructor. (https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib
1100.md)

And exception thrown at runtime:

Unhandled exception. System.InvalidOperationException: Cannot create instance of type '<global namespace>.MyAbstractClass' because it is missing a public instance constructor.
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.BindCore(IConfiguration configuration, MyOptions& instance, Boolean defaultValueIfNotFound, BinderOptions binderOptions) in C:\scratch\bindAbstract\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 144
   at Microsoft.Extensions.Configuration.Binder.SourceGeneration.<BindingExtensions_g>FC276FD7C89D1ED0B2B3E96BAC8F157FABB86A642EAABC54639B357A857C01D08__BindingExtensions.Bind_MyOptions(IConfiguration configuration, Object instance) in C:\scratch\bindAbstract\Microsoft.Extensions.Configuration.Binder.SourceGeneration\Microsoft.Extensions.Configuration.Binder.SourceGeneration.ConfigurationBindingGenerator\BindingExtensions.g.cs:line 48
   at Program.<Main>$(String[] args) in C:\scratch\bindAbstract\Program.cs:line 11

Regression?

No response

Known Workarounds

Disable generator.

Configuration

No response

Other information

No response

Author: ericstj
Assignees: -
Labels:

area-Extensions-Configuration

Milestone: -

@layomia
Copy link
Contributor

layomia commented Sep 15, 2023

Currently unsure how general this issue is... we should test not just abstract types but all types without a usable ctor.

@ericstj
Copy link
Member Author

ericstj commented Sep 15, 2023

Yeah, and compare to what the runtime binder does here. For example if it prefers an existing instance over creating one vs if it prefers creating one if it can. I don't recall what the precedence is.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Oct 6, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Oct 6, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Nov 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants