Open
Description
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;
}
// ....
}