private const string TrimmingWarningMessage = "In case the type is non-primitive, the trimmer cannot statically analyze the object's type so its members may be trimmed.";
private const string InstanceGetTypeTrimmingWarningMessage = "Cannot statically analyze the type of instance so its members may be trimmed";
private const string PropertyTrimmingWarningMessage = "Cannot statically analyze property.PropertyType so its members may be trimmed.";
+ private const string BindSingleElementsToArraySwitch = "Microsoft.Extensions.Configuration.BindSingleElementsToArray";
+
+ // Enable this switch by default.
+ private static bool ShouldBindSingleElementsToArray { get; } = AppContext.TryGetSwitch(BindSingleElementsToArraySwitch, out bool verifyCanBindSingleElementsToArray) ? verifyCanBindSingleElementsToArray : true;
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
return convertedValue;
}
- if (config != null && config.GetChildren().Any())
+ if (config != null && (config.GetChildren().Any() || (configValue != null && ShouldBindSingleElementsToArray)))
{
// If we don't have an instance, try to create one
if (instance == null)
Type itemType = collectionType.GenericTypeArguments[0];
MethodInfo addMethod = collectionType.GetMethod("Add", DeclaredOnlyLookup);
- foreach (IConfigurationSection section in config.GetChildren())
+ foreach (IConfigurationSection section in GetChildrenOrSelf(config))
{
try
{
[RequiresUnreferencedCode("Cannot statically analyze what the element type is of the Array so its members may be trimmed.")]
private static Array BindArray(Array source, IConfiguration config, BinderOptions options)
{
- IConfigurationSection[] children = config.GetChildren().ToArray();
+ IConfigurationSection[] children = GetChildrenOrSelf(config).ToArray();
int arrayLength = source.Length;
Type elementType = source.GetType().GetElementType();
var newArray = Array.CreateInstance(elementType, arrayLength + children.Length);
return property.Name;
}
+
+ private static IEnumerable<IConfigurationSection> GetChildrenOrSelf(IConfiguration config)
+ {
+ if (!ShouldBindSingleElementsToArray)
+ {
+ return config.GetChildren();
+ }
+
+ IEnumerable<IConfigurationSection> children;
+ // If configuration's children is an array, the configuration key will be a number
+ if (config.GetChildren().Any(a => long.TryParse(a.Key, out _)))
+ {
+ children = config.GetChildren();
+ }
+ else
+ {
+ children = new[] { config as IConfigurationSection };
+ }
+
+ return children;
+ }
}
-}
+}
\ No newline at end of file
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
+using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace Microsoft.Extensions.Configuration.Binder.Test
exception.Message);
}
+ [Fact]
+ public void CanBindSingleElementToCollection()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"MyString", "hello world"},
+ {"Nested:Integer", "11"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ IConfiguration config = configurationBuilder.Build();
+
+ var stringArr = config.GetSection("MyString").Get<string[]>();
+ Assert.Equal("hello world", stringArr[0]);
+ Assert.Equal(1, stringArr.Length);
+
+ var stringAsStr = config.GetSection("MyString").Get<string>();
+ Assert.Equal("hello world", stringAsStr);
+
+ var nested = config.GetSection("Nested").Get<NestedOptions>();
+ Assert.Equal(11, nested.Integer);
+
+ var nestedAsArray = config.GetSection("Nested").Get<NestedOptions[]>();
+ Assert.Equal(11, nestedAsArray[0].Integer);
+ Assert.Equal(1, nestedAsArray.Length);
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void CannotBindSingleElementToCollectionWhenSwitchSet()
+ {
+ RemoteExecutor.Invoke(() =>
+ {
+ AppContext.SetSwitch("Microsoft.Extensions.Configuration.BindSingleElementsToArray", false);
+
+ var dic = new Dictionary<string, string>
+ {
+ {"MyString", "hello world"},
+ {"Nested:Integer", "11"},
+ };
+
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ IConfiguration config = configurationBuilder.Build();
+
+ var stringArr = config.GetSection("MyString").Get<string[]>();
+ Assert.Null(stringArr);
+
+ var stringAsStr = config.GetSection("MyString").Get<string>();
+ Assert.Equal("hello world", stringAsStr);
+ }).Dispose();
+ }
+
private interface ISomeInterface
{
}