Smarter binding of Value/Children

if Value cannot be converted to T, it will Bind against the children
This commit is contained in:
Hao Kung 2016-11-16 15:21:52 -08:00
Родитель 1fc1fbad15
Коммит 3af7e8b17b
2 изменённых файлов: 68 добавлений и 35 удалений

Просмотреть файл

@ -244,10 +244,17 @@ namespace Microsoft.Extensions.Configuration
var section = config as IConfigurationSection;
var configValue = section?.Value;
if (configValue != null)
object convertedValue;
Exception error;
if (configValue != null && TryConvertValue(type, configValue, out convertedValue, out error))
{
if (error != null)
{
throw error;
}
// Leaf nodes are always reinitialized
return ConvertValue(type, configValue);
return convertedValue;
}
if (config != null && config.GetChildren().Any())
@ -419,30 +426,52 @@ namespace Microsoft.Extensions.Configuration
return newArray;
}
private static object ConvertValue(Type type, string value)
private static bool TryConvertValue(Type type, string value, out object result, out Exception error)
{
error = null;
result = null;
if (type == typeof(object))
{
return value;
result = value;
return true;
}
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (string.IsNullOrEmpty(value))
{
return null;
return true;
}
return ConvertValue(Nullable.GetUnderlyingType(type), value);
return TryConvertValue(Nullable.GetUnderlyingType(type), value, out result, out error);
}
var converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(typeof(string)))
{
try
{
return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value);
result = converter.ConvertFromInvariantString(value);
}
catch (Exception ex)
{
throw new InvalidOperationException(Resources.FormatError_FailedBinding(value, type), ex);
error = new InvalidOperationException(Resources.FormatError_FailedBinding(value, type), ex);
}
return true;
}
return false;
}
private static object ConvertValue(Type type, string value)
{
object result;
Exception error;
TryConvertValue(type, value, out result, out error);
if (error != null)
{
throw error;
}
return result;
}
private static Type FindOpenGenericInterface(Type expected, Type actual)

Просмотреть файл

@ -464,6 +464,27 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
Assert.Equal("Derived:Sup", options.Virtual);
}
[Fact]
public void CanGetComplexOptionsWhichHasAlsoHasValue()
{
var dic = new Dictionary<string, string>
{
{"obj", "whut" },
{"obj:Integer", "-2"},
{"obj:Boolean", "TRUe"},
{"obj:Nested:Integer", "11"}
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
var options = config.GetSection("obj").Get<ComplexOptions>();
Assert.NotNull(options);
Assert.True(options.Boolean);
Assert.Equal(-2, options.Integer);
Assert.Equal(11, options.Nested.Integer);
}
[Fact]
public void GetCanReadStaticProperty()
{
@ -497,23 +518,6 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
Assert.Equal("other stuff", ComplexOptions.StaticProperty);
}
[Fact]
public void GetComplexOptionsWhichHasAlsoHasValueThrows()
{
var dic = new Dictionary<string, string>
{
{"obj", "whut" },
{"obj:Integer", "-2"},
{"obj:Boolean", "TRUe"},
{"obj:Nested:Integer", "11"}
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
Assert.Throws<InvalidOperationException>(() => config.GetSection("obj").Get<ComplexOptions>());
}
[Theory]
[InlineData("ReadOnly")]
[InlineData("PrivateSetter")]