private static object BindInstance(Type type, object instance, IConfiguration config, BinderOptions options, IEnumerable <ICustomConfigurationBinder> customBinders = null) { // if binding IConfigurationSection, break early if (type == typeof(IConfigurationSection)) { return(config); } var section = config as IConfigurationSection; var configValue = section?.Value; if (configValue != null && TryConvertValue(type, configValue, out var convertedValue, out var error)) { if (error != null) { throw error; } // Leaf nodes are always reinitialized return(convertedValue); } if (config == null || !config.GetChildren().Any()) { return(instance); } // If we don't have an instance, try to create one if (instance == null) { // We are already done if binding to a new collection instance worked instance = AttemptBindToCollectionInterfaces(type, config, options, customBinders); if (instance != null) { return(instance); } instance = CreateInstance(ref type); } // See if its a Dictionary var collectionInterface = FindOpenGenericInterface(typeof(IDictionary <,>), type); if (collectionInterface != null) { BindDictionary(instance, collectionInterface, config, options, customBinders); } else if (type.IsArray) { instance = BindArray((Array)instance, config, options, customBinders); } else { // See if its an ICollection collectionInterface = FindOpenGenericInterface(typeof(ICollection <>), type); if (collectionInterface != null) { BindCollection(instance, collectionInterface, config, options, customBinders); } // Something else else { BindNonScalar(config, ref instance, options, customBinders); } } return(instance); }
private static void SetMembers(IConfiguration configuration, object instance, BinderOptions options, AccessorMembers members, ITypeReadAccessor read, ITypeWriteAccessor write, IEnumerable <ICustomConfigurationBinder> customBinders) { foreach (var member in members) { // We don't support set only, non-public, or indexer properties if (!member.CanRead || member.MemberInfo is MethodInfo method && method.GetParameters().Length > 0) { continue; } var value = read[instance, member.Name]; if (value == null && !member.CanWrite) { // Property doesn't have a value and we cannot set it so there is no // point in going further down the graph continue; } var config = configuration.GetSection(member.Name); value = BindInstance(member.Type, value, config, options, customBinders); if (value == default || !member.CanWrite) { continue; } write.TrySetValue(instance, member.Name, value); } }
// Try to create an array/dictionary instance to back various collection interfaces private static object AttemptBindToCollectionInterfaces(Type type, IConfiguration config, BinderOptions options, IEnumerable <ICustomConfigurationBinder> customBinders) { var typeInfo = type.GetTypeInfo(); if (!typeInfo.IsInterface) { return(null); } var collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyList <>), type); if (collectionInterface != null) { return(BindToCollection(typeInfo, config, options, customBinders)); } collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyDictionary <,>), type); if (collectionInterface != null) { var dictionaryType = typeof(Dictionary <,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]); var instance = CreateInstance(ref dictionaryType); BindDictionary(instance, dictionaryType, config, options, customBinders); return(instance); } collectionInterface = FindOpenGenericInterface(typeof(IDictionary <,>), type); if (collectionInterface != null) { var dictionaryType = typeof(Dictionary <,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]); var instance = CreateInstance(ref dictionaryType); BindDictionary(instance, collectionInterface, config, options, customBinders); return(instance); } collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyCollection <>), type); if (collectionInterface != null) { return(BindToCollection(typeInfo, config, options, customBinders)); } collectionInterface = FindOpenGenericInterface(typeof(ICollection <>), type); if (collectionInterface != null) { return(BindToCollection(typeInfo, config, options, customBinders)); } collectionInterface = FindOpenGenericInterface(typeof(IEnumerable <>), type); return(collectionInterface != null?BindToCollection(typeInfo, config, options, customBinders) : null); }
private static void BindNonScalar(this IConfiguration configuration, ref object instance, BinderOptions options, IEnumerable <ICustomConfigurationBinder> customBinders) { if (instance == null) { return; } var scope = AccessorMemberScope.Public; if (options.BindNonPublicProperties) { scope |= AccessorMemberScope.Private; } var type = instance.GetType(); var read = ReadAccessor.Create(type, AccessorMemberTypes.Properties, scope, out var members); var write = WriteAccessor.Create(type, AccessorMemberTypes.Properties, scope); if (IsTypeDiscriminated(type, out _)) { // Set base properties so the converter has the right values to work with SetMembers(configuration, instance, options, members, read, write, customBinders); // Give a custom converter a chance to change what is bound var converter = TypeDescriptor.GetConverter(type); if (converter.CanConvertFrom(type)) { instance = converter.ConvertFrom(instance); if (instance != null) { type = instance.GetType(); read = ReadAccessor.Create(type, AccessorMemberTypes.Properties, scope, out members); write = WriteAccessor.Create(type, AccessorMemberTypes.Properties, scope); } } else { foreach (var binder in customBinders ?? Enumerable.Empty <ICustomConfigurationBinder>()) { if (!binder.CanConvertFrom(type)) { continue; } var subType = binder.GetTypeFor(instance); if (subType == null) { continue; } type = subType; instance = Instancing.CreateInstance(type); read = ReadAccessor.Create(type, AccessorMemberTypes.Properties, scope, out members); write = WriteAccessor.Create(type, AccessorMemberTypes.Properties, scope); goto setMembers; } } } setMembers: SetMembers(configuration, instance, options, members, read, write, customBinders); }