Skip to content

Commit 9510998

Browse files
[release/7.0] Fix Configuration to ensure calling the property setters. (#80562)
* Fix Configuration to ensure calling the property setters. * Address the feedback * Package authoring Co-authored-by: Tarek Mahmoud Sayed <tarekms@microsoft.com>
1 parent 84b1ddd commit 9510998

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,10 @@ private static void BindProperty(PropertyInfo property, object instance, IConfig
262262
config.GetSection(GetPropertyName(property)),
263263
options);
264264

265-
if (propertyBindingPoint.HasNewValue)
265+
// For property binding, there are some cases when HasNewValue is not set in BindingPoint while a non-null Value inside that object can be retrieved from the property getter.
266+
// As example, when binding a property which not having a configuration entry matching this property and the getter can initialize the Value.
267+
// It is important to call the property setter as the setters can have a logic adjusting the Value.
268+
if (!propertyBindingPoint.IsReadOnly && propertyBindingPoint.Value is not null)
266269
{
267270
property.SetValue(instance, propertyBindingPoint.Value);
268271
}

src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<EnableDefaultItems>true</EnableDefaultItems>
66
<IsPackable>true</IsPackable>
77
<EnableAOTAnalyzer>true</EnableAOTAnalyzer>
8-
<ServicingVersion>2</ServicingVersion>
9-
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
8+
<ServicingVersion>3</ServicingVersion>
9+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1010
<PackageDescription>Functionality to bind an object to data in configuration providers for Microsoft.Extensions.Configuration.</PackageDescription>
1111
</PropertyGroup>
1212

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.Configuration.Test;
46
using System;
57
using System.Collections;
68
using System.Collections.Generic;
@@ -2578,6 +2580,61 @@ public void CanBindPrivatePropertiesFromBaseClass()
25782580
Assert.Equal("a", test.ExposePrivatePropertyValue());
25792581
}
25802582

2583+
[Fact]
2584+
public void EnsureCallingThePropertySetter()
2585+
{
2586+
var json = @"{
2587+
""IPFiltering"": {
2588+
""HttpStatusCode"": 401,
2589+
""Blacklist"": [ ""192.168.0.10-192.168.10.20"", ""fe80::/10"" ]
2590+
}
2591+
}";
2592+
2593+
var configuration = new ConfigurationBuilder()
2594+
.AddJsonStream(TestStreamHelpers.StringToStream(json))
2595+
.Build();
2596+
2597+
OptionWithCollectionProperties options = configuration.GetSection("IPFiltering").Get<OptionWithCollectionProperties>();
2598+
2599+
Assert.NotNull(options);
2600+
Assert.Equal(2, options.Blacklist.Count);
2601+
Assert.Equal("192.168.0.10-192.168.10.20", options.Blacklist.ElementAt(0));
2602+
Assert.Equal("fe80::/10", options.Blacklist.ElementAt(1));
2603+
2604+
Assert.Equal(2, options.ParsedBlacklist.Count); // should be initialized when calling the options.Blacklist setter.
2605+
2606+
Assert.Equal(401, options.HttpStatusCode); // exists in configuration and properly sets the property
2607+
Assert.Equal(2, options.OtherCode); // doesn't exist in configuration. the setter sets default value '2'
2608+
}
2609+
2610+
public class OptionWithCollectionProperties
2611+
{
2612+
private int _otherCode;
2613+
private ICollection<string> blacklist = new HashSet<string>();
2614+
2615+
public ICollection<string> Blacklist
2616+
{
2617+
get => this.blacklist;
2618+
set
2619+
{
2620+
this.blacklist = value ?? new HashSet<string>();
2621+
this.ParsedBlacklist = this.blacklist.Select(b => b).ToList();
2622+
}
2623+
}
2624+
2625+
public int HttpStatusCode { get; set; } = 0;
2626+
2627+
// ParsedBlacklist initialized using the setter of Blacklist.
2628+
public ICollection<string> ParsedBlacklist { get; private set; } = new HashSet<string>();
2629+
2630+
// This property not having any match in the configuration. Still the setter need to be called during the binding.
2631+
public int OtherCode
2632+
{
2633+
get => _otherCode;
2634+
set => _otherCode = value == 0 ? 2 : value;
2635+
}
2636+
}
2637+
25812638
private interface ISomeInterface
25822639
{
25832640
}

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Microsoft.Extensions.Configuration.Binder.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
Link="Common\ConfigurationProviderExtensions.cs" />
1111
<Compile Include="$(LibrariesProjectRoot)Microsoft.Extensions.Configuration\tests\Common\TestStreamHelpers.cs"
1212
Link="Common\TestStreamHelpers.cs" />
13-
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
13+
<Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
1414

1515
<TrimmerRootDescriptor Include="$(MSBuildThisFileDirectory)ILLink.Descriptors.xml" />
1616
</ItemGroup>
1717

1818
<ItemGroup>
1919
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Configuration\src\Microsoft.Extensions.Configuration.csproj" SkipUseReferenceAssembly="true" />
20+
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Configuration.Json\src\Microsoft.Extensions.Configuration.Json.csproj" SkipUseReferenceAssembly="true" />
2021
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.FileProviders.Abstractions\src\Microsoft.Extensions.FileProviders.Abstractions.csproj" SkipUseReferenceAssembly="true" />
2122
<ProjectReference Include="..\src\Microsoft.Extensions.Configuration.Binder.csproj" SkipUseReferenceAssembly="true" />
2223
</ItemGroup>

0 commit comments

Comments
 (0)