Skip to content

Commit c21d01c

Browse files
authored
Merge branch 'release/10.0' into darc-release/10.0-dff90b19-95ba-456f-a4a6-a82885e38181
2 parents a9642f1 + b2842ae commit c21d01c

File tree

8 files changed

+52
-13
lines changed

8 files changed

+52
-13
lines changed

eng/Version.Details.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props
66
<Project>
77
<PropertyGroup>
88
<!-- dotnet/icu dependencies -->
9-
<MicrosoftNETCoreRuntimeICUTransportPackageVersion>10.0.0-rtm.25502.1</MicrosoftNETCoreRuntimeICUTransportPackageVersion>
9+
<MicrosoftNETCoreRuntimeICUTransportPackageVersion>10.0.0-rtm.25553.1</MicrosoftNETCoreRuntimeICUTransportPackageVersion>
1010
<!-- dotnet/wcf dependencies -->
1111
<SystemServiceModelPrimitivesPackageVersion>4.9.0-rc2.21473.1</SystemServiceModelPrimitivesPackageVersion>
1212
<!-- dotnet/llvm-project dependencies -->

eng/Version.Details.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<Dependencies>
22
<Source Uri="https://github.com/dotnet/dotnet" Mapping="runtime" Sha="8ee0cc0bbdbacfeb5b7e703909c9731ff75600f1" BarId="289521" />
33
<ProductDependencies>
4-
<Dependency Name="Microsoft.NETCore.Runtime.ICU.Transport" Version="10.0.0-rtm.25502.1">
4+
<Dependency Name="Microsoft.NETCore.Runtime.ICU.Transport" Version="10.0.0-rtm.25553.1">
55
<Uri>https://github.com/dotnet/icu</Uri>
6-
<Sha>a91b254e70decd379d76338b7bb171ee98301aef</Sha>
6+
<Sha>93ee2e8312a2c5475f5de64a47c3e31024636a05</Sha>
77
</Dependency>
88
<Dependency Name="System.ServiceModel.Primitives" Version="4.9.0-rc2.21473.1">
99
<Uri>https://github.com/dotnet/wcf</Uri>

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,11 +976,13 @@ complexType is not CollectionSpec &&
976976

977977
// The current configuration section doesn't have any children, let's check if we are binding to an array and the configuration value is empty string.
978978
// In this case, we will assign an empty array to the member. Otherwise, we will skip the binding logic.
979-
if (complexType is ArraySpec arraySpec && canSet)
979+
if ((complexType is ArraySpec || complexType.IsExactIEnumerableOfT) && canSet)
980980
{
981+
// Either we have an array or we have an IEnumerable<T> both these types can be assigned an empty array when having empty string configuration value.
982+
Debug.Assert(complexType is ArraySpec || complexType is EnumerableSpec);
981983
string valueIdentifier = GetIncrementalIdentifier(Identifier.value);
982984
EmitStartBlock($@"if ({memberAccessExpr} is null && {Identifier.TryGetConfigurationValue}({configSection}, {Identifier.key}: null, out string? {valueIdentifier}) && {valueIdentifier} == string.Empty)");
983-
_writer.WriteLine($"{memberAccessExpr} = global::System.{Identifier.Array}.Empty<{arraySpec.ElementTypeRef.FullyQualifiedName}>();");
985+
_writer.WriteLine($"{memberAccessExpr} = global::System.{Identifier.Array}.Empty<{((CollectionSpec)complexType).ElementTypeRef.FullyQualifiedName}>();");
984986
EmitEndBlock();
985987
}
986988

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/KnownTypeSymbols.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public KnownTypeSymbols(CSharpCompilation compilation)
8484
Uri = compilation.GetBestTypeByMetadataName(typeof(Uri));
8585
Version = compilation.GetBestTypeByMetadataName(typeof(Version));
8686

87-
// Used to verify input configuation binding API calls.
87+
// Used to verify input configuration binding API calls.
8888
INamedTypeSymbol? binderOptions = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Configuration.BinderOptions");
8989
ActionOfBinderOptions = binderOptions is null ? null : compilation.GetBestTypeByMetadataName(typeof(Action<>))?.Construct(binderOptions);
9090
ConfigurationBinder = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Configuration.ConfigurationBinder");

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/TypeSpec.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ public TypeSpec(ITypeSymbol type)
1717
(DisplayString, FullName) = type.GetTypeNames();
1818
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring();
1919
IsValueType = type.IsValueType;
20-
IsValueTuple = type is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsTupleType;
20+
21+
if (type is INamedTypeSymbol namedTypeSymbol)
22+
{
23+
IsValueTuple = namedTypeSymbol.IsTupleType;
24+
IsExactIEnumerableOfT = namedTypeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T;
25+
}
2126
}
2227

2328
public TypeRef TypeRef { get; }
@@ -39,6 +44,8 @@ public TypeSpec(ITypeSymbol type)
3944
public bool IsValueType { get; }
4045

4146
public bool IsValueTuple { get; }
47+
48+
public bool IsExactIEnumerableOfT { get; }
4249
}
4350

4451
public abstract record ComplexTypeSpec : TypeSpec

src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -503,20 +503,19 @@ private static void BindInstance(
503503
throw new InvalidOperationException(SR.Format(SR.Error_FailedBinding, configValue, section.Path, type));
504504
}
505505
}
506-
else
506+
else if (!bindingPoint.IsReadOnly && bindingPoint.Value is null)
507507
{
508-
if (isParentCollection && bindingPoint.Value is null)
508+
if (isParentCollection)
509509
{
510510
// Try to create the default instance of the type
511511
bindingPoint.TrySetValue(CreateInstance(type, config, options, out _));
512512
}
513-
else if (isConfigurationExist && bindingPoint.Value is null)
513+
else if (isConfigurationExist)
514514
{
515-
// Don't override the existing array in bindingPoint.Value if it is already set.
516-
if (type.IsArray || IsImmutableArrayCompatibleInterface(type))
515+
if (type.IsArray || IsIEnumerableInterface(type))
517516
{
518517
// When having configuration value set to empty string, we create an empty array
519-
bindingPoint.TrySetValue(configValue is null ? null : Array.CreateInstance(type.GetElementType()!, 0));
518+
bindingPoint.TrySetValue(configValue is null ? null : Array.CreateInstance(type.IsArray ? type.GetElementType()! : type.GetGenericArguments()[0], 0));
520519
}
521520
else
522521
{
@@ -1056,6 +1055,9 @@ private static bool IsImmutableArrayCompatibleInterface(Type type)
10561055
|| genericTypeDefinition == typeof(IReadOnlyList<>);
10571056
}
10581057

1058+
private static bool IsIEnumerableInterface(Type type)
1059+
=> type.IsInterface && type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
1060+
10591061
private static bool TypeIsASetInterface(Type type)
10601062
{
10611063
if (!type.IsInterface || !type.IsConstructedGenericType) { return false; }

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,5 +1178,11 @@ public class ArraysContainer
11781178
public byte[] ByteArray2 { get; set; }
11791179
public byte[] ByteArray3 { get; set; }
11801180
}
1181+
1182+
public class MyOptionsWithNullableEnumerable
1183+
{
1184+
public IEnumerable<int>? IEnumerableProperty { get; set; }
1185+
public string[] StringArray { get; set; }
1186+
}
11811187
}
11821188
}

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3038,5 +3038,27 @@ public void TestProvidersOrder()
30383038
Assert.Equal("Provider2A", result.A); // Value should come from the last provider
30393039
Assert.Equal("Provider1B", result.B); // B should not be overridden by the second provider
30403040
}
3041+
3042+
[Fact]
3043+
public void TestBindingEmptyArrayToNullIEnumerable()
3044+
{
3045+
string jsonConfig1 = @"
3046+
{
3047+
""MyService"": {
3048+
""IEnumerableProperty"": [],
3049+
""StringArray"": []
3050+
},
3051+
}";
3052+
3053+
var configuration = new ConfigurationBuilder()
3054+
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonConfig1)))
3055+
.Build().GetSection("MyService");
3056+
3057+
MyOptionsWithNullableEnumerable? result = configuration.Get<MyOptionsWithNullableEnumerable>();
3058+
3059+
Assert.NotNull(result);
3060+
Assert.Equal(Array.Empty<int>(), result.IEnumerableProperty);
3061+
Assert.Equal(Array.Empty<string>(), result.StringArray);
3062+
}
30413063
}
30423064
}

0 commit comments

Comments
 (0)