Skip to content

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

Closed
@ericstj

Description

@ericstj

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions