Skip to content

Microsoft.Extensions.Configuration.Binder has an undocumented breaking change starting in 8.0.0-preview.5.23280.8 regarding enum dictionary keys #89547

@Cyberboss

Description

@Cyberboss

Description

Configuration binder populates dictionaries keyed by enums with their default values if they are unset instead of not adding the entry.

Reproduction Steps

TestLibrary.cs

using System.Collections.Generic;
using System.IO;

using Microsoft.Extensions.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestLibrary
{
	public sealed class EntryHolder
	{
		public int Entry { get; set; }
	}

	public enum TestEnum
	{
		Enum1,
		Enum2,
	}

	public sealed class EnumConfig
	{
		public IDictionary<TestEnum, EntryHolder> EnumHolder { get; set; }
	}


	[TestClass]
	public sealed class TestClass
	{
		static EnumConfig BuildConfig(string yaml)
		{
			var tempFile = Path.GetTempFileName();
			try
			{
				File.WriteAllText(tempFile, yaml);
				var configuration = new ConfigurationBuilder();
				configuration.AddYamlFile(tempFile);
				var section = configuration.Build().GetSection("ConfigSection");
				var config = new EnumConfig();
				section.Bind(config, options => options.ErrorOnUnknownConfiguration = false);
				return config;
			}
			finally
			{
				File.Delete(tempFile);
			}
		}

		[TestMethod]
		public void TestWithoutEntries()
		{
			var configEntry = BuildConfig(@"
ConfigSection:
  EnumHolder:
    Enum1:
    Enum2:
");
			Assert.IsNotNull(configEntry, "EnumConfig was null");
			Assert.IsNotNull(configEntry.EnumHolder, "EnumHolder was null");
			Assert.AreEqual(0, configEntry.EnumHolder.Count);
		}
	}
}

TestLibrary.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0-preview.6.23329.7" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0-preview.5.23280.8" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
    <PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
    <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
    <PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
  </ItemGroup>

</Project>

Expected behavior

The EnumHolder should be empty and tests should pass.

Actual behavior

The tests fails because the dictionary is filled with nulls keyed by TestEnum.

Regression?

Yes!

This behavior does not occur if you downgrade to Microsoft.Extensions.Configuration.Binder version 8.0.0-preview.4.23259.5.

It is not documented as a breaking change.

Known Workarounds

Have your bound class dictionary property explicitly eliminate default initialized values of the KVPs in this case.

Configuration

The problem was first released in Microsoft.Extensions.Configuration.Binder v8.0.0-preview.5.23280.8.

x64 Win 10 Pro 24GB RAM i5-3570K... Is this really necessary?

PS C:\Users\Cyberboss> dotnet --list-sdks
3.1.100 [C:\Program Files\dotnet\sdk]
5.0.214 [C:\Program Files\dotnet\sdk]
6.0.120 [C:\Program Files\dotnet\sdk]
7.0.306 [C:\Program Files\dotnet\sdk]
8.0.100-preview.6.23330.14 [C:\Program Files\dotnet\sdk]
PS C:\Users\Cyberboss> dotnet --version
8.0.100-preview.6.23330.14

Other information

One of these two PRs is to blame if my git detective skills are decent. Probably the first:

#86485
#85843

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions