dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.17k stars 4.72k forks source link

ConfigurationBinder ignores [ConfigurationKeyName] on derived type #107247

Open bart-vmware opened 1 month ago

bart-vmware commented 1 month ago

Description

When binding a configuration section to a derived class that overrides a virtual property, the [ConfigurationKeyName("alternate-name")] annotation on the derived property is not honored.

Reproduction Steps

using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["root:alternate-name"] = "some"
    })
    .Build();

var derived = new Derived();

var section = configuration.GetSection("root");
section.Bind(derived);

// Prints "Name=" instead of "Name=some"
Console.WriteLine($"Name={derived.Name}");

public class Base
{
    public virtual string? Name { get; set; }
}

public class Derived : Base
{
    [ConfigurationKeyName("alternate-name")]
    public override string? Name { get; set; }
}

Expected behavior

The alternate configuration key name in [ConfigurationKeyName] on the derived property is honored.

Actual behavior

The alternate configuration key name in [ConfigurationKeyName] on the derived property is ignored.

Regression?

No response

Known Workarounds

No response

Configuration

dotnet --info
.NET SDK:
 Version:           8.0.401
 Commit:            811edcc344
 Workload version:  8.0.400-manifests.57f7c351
 MSBuild version:   17.11.4+37eb419ad

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22631
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\8.0.401\

.NET workloads installed:
Configured to use loose manifests when installing new manifests.
 [aspire]
   Installation Source: SDK 8.0.400, VS 17.11.35222.181
   Manifest Version:    8.2.0/8.0.100
   Manifest Path:       C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.0\WorkloadManifest.json
   Install Type:        FileBased

Host:
  Version:      8.0.8
  Architecture: x64
  Commit:       08338fcaa5

.NET SDKs installed:
  8.0.400 [C:\Program Files\dotnet\sdk]
  8.0.401 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 6.0.33 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 8.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

Other information

I've traced this down to ConfigurationBinder.GetAllProperties:

// if the property is virtual, only add the base-most definition so
// overridden properties aren't duplicated in the list.
MethodInfo? setMethod = property.GetSetMethod(true);

if (setMethod is null || !setMethod.IsVirtual || setMethod == setMethod.GetBaseDefinition())
{
    allProperties.Add(property);
}

Because the derived PropertyInfo is not being added, ConfigurationBinder.GetPropertyName won't find the attribute:

// Check for a custom property name used for configuration key binding
foreach (var attributeData in property.GetCustomAttributesData())
{
    if (attributeData.AttributeType != typeof(ConfigurationKeyNameAttribute))
    {
        continue;
    }

    // ....
}
dotnet-policy-service[bot] commented 1 month ago

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