Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Dunn <steve@dunnhq.com>2022-11-07 02:08:52 +0300
committerGitHub <noreply@github.com>2022-11-07 02:08:52 +0300
commit264d7391ec9f6e698051db0621c5e090d0ae4710 (patch)
tree282613e42228b17414a2fa8d8558b6fb122d5176
parentfb001c47d8fe6575c390fa7fc47b039917882841 (diff)
Control if invalid configuration values are swallowed or thrown (#77708)
-rw-r--r--src/libraries/Microsoft.Extensions.Configuration.Binder/src/BinderOptions.cs6
-rw-r--r--src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs29
-rw-r--r--src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx3
-rw-r--r--src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs123
4 files changed, 146 insertions, 15 deletions
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/BinderOptions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/BinderOptions.cs
index f5ffd6eb575..b4664675b15 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/BinderOptions.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/BinderOptions.cs
@@ -15,10 +15,10 @@ namespace Microsoft.Extensions.Configuration
public bool BindNonPublicProperties { get; set; }
/// <summary>
- /// When false (the default), no exceptions are thrown when a configuration key is found for which the
- /// provided model object does not have an appropriate property which matches the key's name.
+ /// When false (the default), no exceptions are thrown when trying to convert a value or when a configuration
+ /// key is found for which the provided model object does not have an appropriate property which matches the key's name.
/// When true, an <see cref="System.InvalidOperationException"/> is thrown with a description
- /// of the missing properties.
+ /// of the error.
/// </summary>
public bool ErrorOnUnknownConfiguration { get; set; }
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
index 1d0992781f0..332f74ef794 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
@@ -620,8 +620,13 @@ namespace Microsoft.Extensions.Configuration
setter.SetValue(dictionary, valueBindingPoint.Value, new object[] { key });
}
}
- catch
+ catch(Exception ex)
{
+ if (options.ErrorOnUnknownConfiguration)
+ {
+ throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
+ nameof(options.ErrorOnUnknownConfiguration)), ex);
+ }
}
}
}
@@ -653,8 +658,14 @@ namespace Microsoft.Extensions.Configuration
addMethod?.Invoke(collection, new[] { itemBindingPoint.Value });
}
}
- catch
+ catch(Exception ex)
{
+ if (options.ErrorOnUnknownConfiguration)
+ {
+ throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
+ nameof(options.ErrorOnUnknownConfiguration)), ex);
+ }
+
}
}
}
@@ -702,8 +713,13 @@ namespace Microsoft.Extensions.Configuration
list.Add(itemBindingPoint.Value);
}
}
- catch
+ catch (Exception ex)
{
+ if (options.ErrorOnUnknownConfiguration)
+ {
+ throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
+ nameof(options.ErrorOnUnknownConfiguration)), ex);
+ }
}
}
@@ -761,8 +777,13 @@ namespace Microsoft.Extensions.Configuration
addMethod.Invoke(instance, arguments);
}
}
- catch
+ catch (Exception ex)
{
+ if (options.ErrorOnUnknownConfiguration)
+ {
+ throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
+ nameof(options.ErrorOnUnknownConfiguration)), ex);
+ }
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
index 197b325252e..a926fc42386 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
@@ -132,6 +132,9 @@
<data name="Error_FailedToActivate" xml:space="preserve">
<value>Failed to create instance of type '{0}'.</value>
</data>
+ <data name="Error_GeneralErrorWhenBinding" xml:space="preserve">
+ <value>'{0}' was set and binding has failed. The likely cause is an invalid configuration value.</value>
+ </data>
<data name="Error_MissingConfig" xml:space="preserve">
<value>'{0}' was set on the provided {1}, but the following properties were not found on the instance of {2}: {3}</value>
</data>
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs
index a24e7d3b9a3..51a25429f77 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs
@@ -75,20 +75,20 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
public ISet<string> ISetNoSetter { get; } = new HashSet<string>();
public HashSet<string> InstantiatedHashSetWithSomeValues { get; set; } =
- new HashSet<string>(new[] {"existing1", "existing2"});
+ new HashSet<string>(new[] { "existing1", "existing2" });
public SortedSet<string> InstantiatedSortedSetWithSomeValues { get; set; } =
- new SortedSet<string>(new[] {"existing1", "existing2"});
+ new SortedSet<string>(new[] { "existing1", "existing2" });
public SortedSet<string> NonInstantiatedSortedSetWithSomeValues { get; set; } = null!;
public ISet<string> InstantiatedISetWithSomeValues { get; set; } =
new HashSet<string>(new[] { "existing1", "existing2" });
-
+
public ISet<UnsupportedTypeInHashSet> HashSetWithUnsupportedKey { get; set; } =
new HashSet<UnsupportedTypeInHashSet>();
- public ISet<UnsupportedTypeInHashSet> UninstantiatedHashSetWithUnsupportedKey { get; set; }
+ public ISet<UnsupportedTypeInHashSet> UninstantiatedHashSetWithUnsupportedKey { get; set; }
#if NETCOREAPP
public IReadOnlySet<string> InstantiatedIReadOnlySet { get; set; } = new HashSet<string>();
@@ -348,7 +348,7 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
}
public string Color { get; set; }
- public int Length { get; set; }
+ public int Length { get; set; }
}
public class ImmutableLengthAndColorClass
@@ -505,6 +505,113 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
Option2,
}
+ public class CollectionsBindingWithErrorOnUnknownConfiguration
+ {
+ public class MyModelContainingArray
+ {
+ public TestSettingsEnum[] Enums { get; set; }
+ }
+
+ public class MyModelContainingADictionary
+ {
+ public Dictionary<string, TestSettingsEnum> Enums { get; set; }
+ }
+
+ [Fact]
+ public void WithFlagUnset_NoExceptionIsThrownWhenFailingToParseEnumsInAnArrayAndValidItemsArePreserved()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"Section:Enums:0", "Option1"},
+ {"Section:Enums:1", "Option3"}, // invalid - ignored
+ {"Section:Enums:2", "Option4"}, // invalid - ignored
+ {"Section:Enums:3", "Option2"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+ var configSection = config.GetSection("Section");
+
+ var model = configSection.Get<MyModelContainingArray>(o => o.ErrorOnUnknownConfiguration = false);
+
+ Assert.Equal(2, model.Enums.Length);
+ Assert.Equal(TestSettingsEnum.Option1, model.Enums[0]);
+ Assert.Equal(TestSettingsEnum.Option2, model.Enums[1]);
+ }
+
+ [Fact]
+ public void WithFlagUnset_NoExceptionIsThrownWhenFailingToParseEnumsInADictionaryAndValidItemsArePreserved()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"Section:Enums:First", "Option1"},
+ {"Section:Enums:Second", "Option3"}, // invalid - ignored
+ {"Section:Enums:Third", "Option4"}, // invalid - ignored
+ {"Section:Enums:Fourth", "Option2"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+ var configSection = config.GetSection("Section");
+
+ var model = configSection.Get<MyModelContainingADictionary>(o =>
+ o.ErrorOnUnknownConfiguration = false);
+
+ Assert.Equal(2, model.Enums.Count);
+ Assert.Equal(TestSettingsEnum.Option1, model.Enums["First"]);
+ Assert.Equal(TestSettingsEnum.Option2, model.Enums["Fourth"]);
+ }
+
+ [Fact]
+ public void WithFlagSet_AnExceptionIsThrownWhenFailingToParseEnumsInAnArray()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"Section:Enums:0", "Option1"},
+ {"Section:Enums:1", "Option3"}, // invalid - exception thrown
+ {"Section:Enums:2", "Option1"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+ var configSection = config.GetSection("Section");
+
+ var exception = Assert.Throws<InvalidOperationException>(
+ () => configSection.Get<MyModelContainingArray>(o => o.ErrorOnUnknownConfiguration = true));
+
+ Assert.Equal(
+ SR.Format(SR.Error_GeneralErrorWhenBinding, nameof(BinderOptions.ErrorOnUnknownConfiguration)),
+ exception.Message);
+ }
+
+ [Fact]
+ public void WithFlagSet_AnExceptionIsThrownWhenFailingToParseEnumsInADictionary()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"Section:Enums:First", "Option1"},
+ {"Section:Enums:Second", "Option3"}, // invalid - exception thrown
+ {"Section:Enums:Third", "Option1"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+ var configSection = config.GetSection("Section");
+
+ var exception = Assert.Throws<InvalidOperationException>(
+ () => configSection.Get<MyModelContainingADictionary>(o =>
+ o.ErrorOnUnknownConfiguration = true));
+
+ Assert.Equal(
+ SR.Format(SR.Error_GeneralErrorWhenBinding, nameof(BinderOptions.ErrorOnUnknownConfiguration)),
+ exception.Message);
+ }
+ }
+
public record RootConfig(NestedConfig Nested);
public record NestedConfig(string MyProp);
@@ -804,7 +911,7 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
public class Foo
{
public IReadOnlyDictionary<string, int> Items { get; set; } =
- new Dictionary<string, int> {{"existing-item1", 1}, {"existing-item2", 2}};
+ new Dictionary<string, int> { { "existing-item1", 1 }, { "existing-item2", 2 } };
}
@@ -829,7 +936,7 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
Assert.Equal(3, options.Items["item3"]);
Assert.Equal(4, options.Items["item4"]);
-
+
}
[Fact]
@@ -944,7 +1051,7 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
Assert.Equal(3, options.NonInstantiatedReadOnlyDictionary["item3"]);
Assert.Equal(4, options.NonInstantiatedReadOnlyDictionary["item4"]);
}
-
+
[Fact]
public void CanBindNonInstantiatedDictionaryOfISet()