Skip to content

Configuration Binding Generator produces non-compiling code for required property in child classes #128390

@Sebbs128

Description

@Sebbs128

Description

The source-generated configuration binding extensions produces a CS0103 ("The name '' does not exist in the current context.") for required properties in child classes. The child class has a constructor with SetsRequiredMembers set, and the generated BindingExtensions.g.cs correctly calls this, but curiously it also attempts to set the property (which is where the compiler error points to).

BindingExtensions.g.cs

public static global::GreetSettings InitializeGreetSettings(IConfiguration configuration, BinderOptions? binderOptions)
{
    if (configuration["Name"] is not string name)
    {
        throw new InvalidOperationException("Cannot create instance of type 'GreetSettings' because parameter 'name' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.");
    }

    return new global::GreetSettings(name)
    {
        Name = Name, // CS0103 The name 'Name' does not exist in the current context
    };
}

Reproduction Steps

TrimRequiredMinRepro.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>

        <PublishSingleFile>true</PublishSingleFile>
        <PublishSelfContained>true</PublishSelfContained>
        <PublishTrimmed>true</PublishTrimmed>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.8" />
    </ItemGroup>
</Project>

Program.cs

using System.Diagnostics.CodeAnalysis;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.Configure<Settings>(builder.Configuration.GetRequiredSection(nameof(Settings)));

var app = builder.Build();

var settings = app.Services.GetRequiredService<IOptions<Settings>>();

Console.WriteLine($"{settings.Value.GreetPerson.Greeting} {settings.Value.GreetPerson.Name}");

public class Settings
{
    public GreetSettings GreetPerson { get; set; } = new("Bob");
}

[method: SetsRequiredMembers]
public class GreetSettings(string name)
{
    public string Greeting { get; set; } = "Hello";
    public required string Name { get; set; } = name;
}

Expected behavior

The code should compile. Additionally, required properties set through the constructor shouldn't be attempted to be set through object initialisation in the generated configuration binder.

BindingExtensions.g.cs

public static global::GreetSettings InitializeGreetSettings(IConfiguration configuration, BinderOptions? binderOptions)
{
    if (configuration["Name"] is not string name)
    {
        throw new InvalidOperationException("Cannot create instance of type 'GreetSettings' because parameter 'name' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.");
    }

    return new global::GreetSettings(name);
}

Actual behavior

The generated configuration binder has a compilation error (CS0103).

Regression?

Error still occurs with Microsoft.Extensions.Configuration.Binder >= 10.0.0, as well as net9.0 tfm and 9.0.0 packages.

Known Workarounds

Removing required from the property no longer causes the error. Also, changing the constructor parameter name to exactly match the property name (eg. name => Name) also works around the issue.

Configuration

dotnet --version: 10.0.204
OS and version: Windows 10.0.22621
Architecture: x64

Also reproducible on
dotnet --version: 10.0.107
OS and version: Ubuntu 22.04 (via WSL)
Architecture: x64

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions