Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ public class BinderOptions
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; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,13 @@ private static void BindConcreteDictionary(
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);
}
}
}
}
Expand Down Expand Up @@ -653,8 +658,14 @@ private static void BindCollection(
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);
}

}
}
}
Expand Down Expand Up @@ -702,8 +713,13 @@ private static Array BindArray(Type type, IEnumerable? source, IConfiguration co
list.Add(itemBindingPoint.Value);
}
}
catch
catch (Exception ex)
{
if (options.ErrorOnUnknownConfiguration)
{
throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
nameof(options.ErrorOnUnknownConfiguration)), ex);
}
}
}

Expand Down Expand Up @@ -761,8 +777,13 @@ private static Array BindArray(Type type, IEnumerable? source, IConfiguration co
addMethod.Invoke(instance, arguments);
}
}
catch
catch (Exception ex)
{
if (options.ErrorOnUnknownConfiguration)
{
throw new InvalidOperationException(SR.Format(SR.Error_GeneralErrorWhenBinding,
nameof(options.ErrorOnUnknownConfiguration)), ex);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,20 @@ public string ReadOnly
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>();
Expand Down Expand Up @@ -348,7 +348,7 @@ public MutableStructWithConstructor(string randomParameter)
}

public string Color { get; set; }
public int Length { get; set; }
public int Length { get; set; }
}

public class ImmutableLengthAndColorClass
Expand Down Expand Up @@ -505,6 +505,113 @@ public enum TestSettingsEnum
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);
Expand Down Expand Up @@ -804,7 +911,7 @@ public void CanBindInstantiatedDictionaryOfIReadOnlySetWithSomeExistingValues()
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 } };

}

Expand All @@ -829,7 +936,7 @@ public void CanBindInstantiatedReadOnlyDictionary2()
Assert.Equal(3, options.Items["item3"]);
Assert.Equal(4, options.Items["item4"]);


}

[Fact]
Expand Down Expand Up @@ -944,7 +1051,7 @@ public void CanBindNonInstantiatedReadOnlyDictionary()
Assert.Equal(3, options.NonInstantiatedReadOnlyDictionary["item3"]);
Assert.Equal(4, options.NonInstantiatedReadOnlyDictionary["item4"]);
}


[Fact]
public void CanBindNonInstantiatedDictionaryOfISet()
Expand Down