Skip to content

ConfigurationBinder: Behavior change when binding arrays with invalid elements (.NET 6 → .NET 8) #124169

@kyogo-hayashi

Description

@kyogo-hayashi

Description

Hello, I have a question regarding the behavior of IConfiguration.Get().
In a .NET application, we use IConfiguration.Get() (which internally uses ConfigurationBinder) to read configuration values.
When upgrading the target framework from .NET 6 to .NET 8, and updating the Microsoft.Extensions.Configuration packages from 6.x to 8.x, we noticed that the binding behavior for array (or array-like) properties has changed.

1. What I already understand (Breaking change)

  
I’m aware of the .NET 8 breaking change where:
ConfigurationBinder.Get<T>(..., options => options.ErrorOnUnknownConfiguration = true)

will now throw an exception when a value cannot be converted to the destination type.
However, the behavior I’m asking about is the default behavior of IConfiguration.Get() when no BinderOptions are specified.

2. What I would like to confirm

With the default IConfiguration.Get() (no options), the behavior when an array element fails type conversion seems to differ
between .NET 6 and .NET 8.
Is this behavior:

  • an intended change in .NET 7/8+,a regression, or a documentation gap?

Reproduction Steps

Reproduction (simplified)

appsettings.json

{
  "ApiUrl": "https://example.com/api",
  "Timeout": 5,
  "OutputPath": "./output",
  "List": [
    {
      "Name": "A",
      "Address": "AA",
      "Interval": 10
    },
    {
      "Name": "B",
      "Address": "BB",
      "Interval": "a"
    }
  ]
} 

Settings classes

public class SampleSettings
{
    public string ApiUrl { get; set; }
    public int Timeout { get; set; }
    public string OutputPath { get; set; }
    public ListSetting[] List { get; set; }
}

public class ListSetting
{
    public string Name { get; set; }
    public string Address { get; set; }
    public int Interval { get; set; }
}

Loading method

public static SampleSettings LoadSettings(IConfiguration configuration)
{
    SampleSettings settings = configuration.Get<SampleSettings>();
    return settings;
}

Then, run the loading method shown above to bind the configuration.

Expected behavior

.NET 6 Expected behavior

List[0] = { Name = "A", Address = "AA", Interval = 10 }
List[1] = null   // Conversion failed but null placeholder remains (array length 2)

Actual behavior

.NET 8 Actual behavior

List[0] = { Name = "A", Address = "AA", Interval = 10 }
List[1] is skipped entirely (array length becomes 1)

Note: I also tested this behavior on .NET 10, and it behaves the same.

Regression?

No response

Known Workarounds

No response

Configuration

NET versions tested:

  • .NET 6
  • .NET 8

Other information

Additional notes (speculated cause)

After checking the public ConfigurationBinder implementation in dotnet/runtime, it appears that:

In .NET 6, the binder pre-allocated the array and attempted to bind each element, resulting in failed elements becoming null.
In .NET 8, the binder first collects only successfully bound elements into a list, and then materializes the final array afterwards.

This difference may explain why failed elements are no longer preserved as null, but instead skipped.
This is only an observation based on the public source code.

Questions

  1. For the default IConfiguration.Get() (no BinderOptions),
    is it intended that elements which fail type conversion are now skipped rather than preserved as null?

  2. If this is an intended behavior change,from which
    .NET version does this change originate?

If this is simply my misunderstanding or a documentation oversight, I apologize.
Thank you very much for your help.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions