dotnet / dotnet/runtime

Binding of empty string is inconsistent between reflection and source generator configuration binders

Open
#132,968 1 comment 1 reaction 0 assignees View on GitHub
area-Extensions-Configuration bug
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

The following tests fail for the source generated configuration binder, but succeed for the reflection binder, showing inconsistencies between the two binders. I think these should be fixed. It's likely that some fixes will require changing the source generator, and others changing the reflection binder. (In other words, one of these tests succeeding doesn't necessarily mean it's the correct behavior.)

```c#
[Fact]
public void GetValue_EmptyStringForString_ReturnsEmptyString()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Value": "" }""");

Assert.Equal(string.Empty, config.GetValue("Value"));
}

[Fact]
public void GetValue_EmptyStringForInt32_Throws()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Value": "" }""");

Assert.Throws(() => config.GetValue("Value"));
}

[Fact]
public void GetValue_EmptyStringForByteArray_ReturnsEmptyArray()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Value": "" }""");

Assert.Empty(Assert.IsType(config.GetValue("Value")));
}

[Fact]
public void Get_EmptyStringForNullableInt32_ReturnsNull()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Value": "" }""");

Assert.Null(config.GetSection("Value").Get());
}

[Fact]
public void Get_EmptyStringForInt32_ThrowsConversionException()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Value": "" }""");

Assert.Throws(() => config.GetSection("Value").Get());
}

[Fact]
public void Get_EmptyStringArrayElementWithErrorOnUnknownConfiguration_Throws()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Values": [ "" ] }""");

Assert.Throws(
() => config.GetSection("Values").Get(o => o.ErrorOnUnknownConfiguration = true));
}

[Fact]
public void Get_EmptyStringDictionaryValueWithErrorOnUnknownConfiguration_Throws()
{
IConfiguration config = TestHelpers.GetConfigurationFromJsonString("""{ "Values": { "Key": "" } }""");

Assert.Throws(
() => config.GetSection("Values").Get>(o => o.ErrorOnUnknownConfiguration = true));
}
```

Contributor guide

Open the contributing guide

Research direction

Start by running the listed configuration binder tests with the source-generated and reflection binders, using TestHelpers.GetConfigurationFromJsonString and the GetValue/Get entry points. Compare each empty-string result and determine the intended behavior before changing either implementation. Done means both binders consistently handle strings, numeric values, byte arrays, nullable values, arrays, and dictionary values, with the expected exceptions.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.