Smarter binding of Value/Children
if Value cannot be converted to T, it will Bind against the children
This commit is contained in:
Родитель
1fc1fbad15
Коммит
3af7e8b17b
|
@ -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
|
||||
{
|
||||
result = converter.ConvertFromInvariantString(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = new InvalidOperationException(Resources.FormatError_FailedBinding(value, type), ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
private static object ConvertValue(Type type, string value)
|
||||
{
|
||||
object result;
|
||||
Exception error;
|
||||
TryConvertValue(type, value, out result, out error);
|
||||
if (error != null)
|
||||
{
|
||||
return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatError_FailedBinding(value, type), ex);
|
||||
throw error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Type FindOpenGenericInterface(Type expected, Type actual)
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
|
|||
public class ConfigurationInterfaceOptions
|
||||
{
|
||||
public IConfigurationSection Section { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class DerivedOptionsWithIConfigurationSection : DerivedOptions
|
||||
{
|
||||
|
@ -388,10 +388,10 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
|
|||
var configurationBuilder = new ConfigurationBuilder();
|
||||
configurationBuilder.AddInMemoryCollection(dic);
|
||||
var config = configurationBuilder.Build();
|
||||
|
||||
|
||||
var instance = new ComplexOptions();
|
||||
config.Bind(instance);
|
||||
|
||||
|
||||
Assert.True(instance.Boolean);
|
||||
Assert.Equal(-2, instance.Integer);
|
||||
Assert.Equal(11, instance.Nested.Integer);
|
||||
|
@ -431,10 +431,10 @@ namespace Microsoft.Extensions.Configuration.Binder.Test
|
|||
var configurationBuilder = new ConfigurationBuilder();
|
||||
configurationBuilder.AddInMemoryCollection(dic);
|
||||
var config = configurationBuilder.Build();
|
||||
|
||||
|
||||
var instance = new DerivedOptions();
|
||||
config.Bind(instance);
|
||||
|
||||
|
||||
Assert.True(instance.Boolean);
|
||||
Assert.Equal(-2, instance.Integer);
|
||||
Assert.Equal(11, instance.Nested.Integer);
|
||||
|
@ -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")]
|
||||
|
|
Загрузка…
Ссылка в новой задаче