• netcore3.0 IConfiguration配置源码解析(三)


    前面两篇文章主要讲到netcore的配置以及各种配置源。

    本篇主要讲到把配置值转换成C#的实体类,体现在IConfiguration各种扩展方法:

    public static class ConfigurationBinder
        {
            /// <summary>
            /// Attempts to bind the configuration instance to a new instance of type T.
            /// If this configuration section has a value, that will be used.
            /// Otherwise binding by matching property names against configuration keys recursively.
            /// </summary>
            /// <typeparam name="T">The type of the new instance to bind.</typeparam>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <returns>The new instance of T if successful, default(T) otherwise.</returns>
            public static T Get<T>(this IConfiguration configuration)
                => configuration.Get<T>(_ => { });
    
            /// <summary>
            /// Attempts to bind the configuration instance to a new instance of type T.
            /// If this configuration section has a value, that will be used.
            /// Otherwise binding by matching property names against configuration keys recursively.
            /// </summary>
            /// <typeparam name="T">The type of the new instance to bind.</typeparam>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="configureOptions">Configures the binder options.</param>
            /// <returns>The new instance of T if successful, default(T) otherwise.</returns>
            public static T Get<T>(this IConfiguration configuration, Action<BinderOptions> configureOptions)
            {
                if (configuration == null)
                {
                    throw new ArgumentNullException(nameof(configuration));
                }
    
                var result = configuration.Get(typeof(T), configureOptions);
                if (result == null)
                {
                    return default(T);
                }
                return (T)result;
            }
    
            /// <summary>
            /// Attempts to bind the configuration instance to a new instance of type T.
            /// If this configuration section has a value, that will be used.
            /// Otherwise binding by matching property names against configuration keys recursively.
            /// </summary>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="type">The type of the new instance to bind.</param>
            /// <returns>The new instance if successful, null otherwise.</returns>
            public static object Get(this IConfiguration configuration, Type type)
                => configuration.Get(type, _ => { });
    
            /// <summary>
            /// Attempts to bind the configuration instance to a new instance of type T.
            /// If this configuration section has a value, that will be used.
            /// Otherwise binding by matching property names against configuration keys recursively.
            /// </summary>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="type">The type of the new instance to bind.</param>
            /// <param name="configureOptions">Configures the binder options.</param>
            /// <returns>The new instance if successful, null otherwise.</returns>
            public static object Get(this IConfiguration configuration, Type type, Action<BinderOptions> configureOptions)
            {
                if (configuration == null)
                {
                    throw new ArgumentNullException(nameof(configuration));
                }
    
                var options = new BinderOptions();
                configureOptions?.Invoke(options);
                return BindInstance(type, instance: null, config: configuration, options: options);
            }
    
            /// <summary>
            /// Attempts to bind the given object instance to the configuration section specified by the key by matching property names against configuration keys recursively.
            /// </summary>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="key">The key of the configuration section to bind.</param>
            /// <param name="instance">The object to bind.</param>
            public static void Bind(this IConfiguration configuration, string key, object instance)
                => configuration.GetSection(key).Bind(instance);
    
            /// <summary>
            /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
            /// </summary>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="instance">The object to bind.</param>
            public static void Bind(this IConfiguration configuration, object instance)
                => configuration.Bind(instance, o => { });
    
            /// <summary>
            /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
            /// </summary>
            /// <param name="configuration">The configuration instance to bind.</param>
            /// <param name="instance">The object to bind.</param>
            /// <param name="configureOptions">Configures the binder options.</param>
            public static void Bind(this IConfiguration configuration, object instance, Action<BinderOptions> configureOptions)
            {
                if (configuration == null)
                {
                    throw new ArgumentNullException(nameof(configuration));
                }
    
                if (instance != null)
                {
                    var options = new BinderOptions();
                    configureOptions?.Invoke(options);
                    BindInstance(instance.GetType(), instance, configuration, options);
                }
            }
    
            /// <summary>
            /// Extracts the value with the specified key and converts it to type T.
            /// </summary>
            /// <typeparam name="T">The type to convert the value to.</typeparam>
            /// <param name="configuration">The configuration.</param>
            /// <param name="key">The key of the configuration section's value to convert.</param>
            /// <returns>The converted value.</returns>
            public static T GetValue<T>(this IConfiguration configuration, string key)
            {
                return GetValue(configuration, key, default(T));
            }
    
            /// <summary>
            /// Extracts the value with the specified key and converts it to type T.
            /// </summary>
            /// <typeparam name="T">The type to convert the value to.</typeparam>
            /// <param name="configuration">The configuration.</param>
            /// <param name="key">The key of the configuration section's value to convert.</param>
            /// <param name="defaultValue">The default value to use if no value is found.</param>
            /// <returns>The converted value.</returns>
            public static T GetValue<T>(this IConfiguration configuration, string key, T defaultValue)
            {
                return (T)GetValue(configuration, typeof(T), key, defaultValue);
            }
    
            /// <summary>
            /// Extracts the value with the specified key and converts it to the specified type.
            /// </summary>
            /// <param name="configuration">The configuration.</param>
            /// <param name="type">The type to convert the value to.</param>
            /// <param name="key">The key of the configuration section's value to convert.</param>
            /// <returns>The converted value.</returns>
            public static object GetValue(this IConfiguration configuration, Type type, string key)
            {
                return GetValue(configuration, type, key, defaultValue: null);
            }
    
            /// <summary>
            /// Extracts the value with the specified key and converts it to the specified type.
            /// </summary>
            /// <param name="configuration">The configuration.</param>
            /// <param name="type">The type to convert the value to.</param>
            /// <param name="key">The key of the configuration section's value to convert.</param>
            /// <param name="defaultValue">The default value to use if no value is found.</param>
            /// <returns>The converted value.</returns>
            public static object GetValue(this IConfiguration configuration, Type type, string key, object defaultValue)
            {
                var section = configuration.GetSection(key);
                var value = section.Value;
                if (value != null)
                {
                    return ConvertValue(type, value, section.Path);
                }
                return defaultValue;
            }
    
            private static void BindNonScalar(this IConfiguration configuration, object instance, BinderOptions options)
            {
                if (instance != null)
                {
                    foreach (var property in GetAllProperties(instance.GetType().GetTypeInfo()))
                    {
                        BindProperty(property, instance, configuration, options);
                    }
                }
            }
    
            private static void BindProperty(PropertyInfo property, object instance, IConfiguration config, BinderOptions options)
            {
                // We don't support set only, non public, or indexer properties
                if (property.GetMethod == null ||
                    (!options.BindNonPublicProperties && !property.GetMethod.IsPublic) ||
                    property.GetMethod.GetParameters().Length > 0)
                {
                    return;
                }
    
                var propertyValue = property.GetValue(instance);
                var hasSetter = property.SetMethod != null && (property.SetMethod.IsPublic || options.BindNonPublicProperties);
    
                if (propertyValue == null && !hasSetter)
                {
                    // Property doesn't have a value and we cannot set it so there is no
                    // point in going further down the graph
                    return;
                }
    
                propertyValue = BindInstance(property.PropertyType, propertyValue, config.GetSection(property.Name), options);
    
                if (propertyValue != null && hasSetter)
                {
                    property.SetValue(instance, propertyValue);
                }
            }
    
            private static object BindToCollection(TypeInfo typeInfo, IConfiguration config, BinderOptions options)
            {
                var type = typeof(List<>).MakeGenericType(typeInfo.GenericTypeArguments[0]);
                var instance = Activator.CreateInstance(type);
                BindCollection(instance, type, config, options);
                return instance;
            }
    
            // Try to create an array/dictionary instance to back various collection interfaces
            private static object AttemptBindToCollectionInterfaces(Type type, IConfiguration config, BinderOptions options)
            {
                var typeInfo = type.GetTypeInfo();
    
                if (!typeInfo.IsInterface)
                {
                    return null;
                }
    
                var collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyList<>), type);
                if (collectionInterface != null)
                {
                    // IEnumerable<T> is guaranteed to have exactly one parameter
                    return BindToCollection(typeInfo, config, options);
                }
    
                collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyDictionary<,>), type);
                if (collectionInterface != null)
                {
                    var dictionaryType = typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]);
                    var instance = Activator.CreateInstance(dictionaryType);
                    BindDictionary(instance, dictionaryType, config, options);
                    return instance;
                }
    
                collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type);
                if (collectionInterface != null)
                {
                    var instance = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeInfo.GenericTypeArguments[0], typeInfo.GenericTypeArguments[1]));
                    BindDictionary(instance, collectionInterface, config, options);
                    return instance;
                }
    
                collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyCollection<>), type);
                if (collectionInterface != null)
                {
                    // IReadOnlyCollection<T> is guaranteed to have exactly one parameter
                    return BindToCollection(typeInfo, config, options);
                }
    
                collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type);
                if (collectionInterface != null)
                {
                    // ICollection<T> is guaranteed to have exactly one parameter
                    return BindToCollection(typeInfo, config, options);
                }
    
                collectionInterface = FindOpenGenericInterface(typeof(IEnumerable<>), type);
                if (collectionInterface != null)
                {
                    // IEnumerable<T> is guaranteed to have exactly one parameter
                    return BindToCollection(typeInfo, config, options);
                }
    
                return null;
            }
    
            private static object BindInstance(Type type, object instance, IConfiguration config, BinderOptions options)
            {
                // if binding IConfigurationSection, break early
                if (type == typeof(IConfigurationSection))
                {
                    return config;
                }
    
                var section = config as IConfigurationSection;
                var configValue = section?.Value;
                object convertedValue;
                Exception error;
                if (configValue != null && TryConvertValue(type, configValue, section.Path, out convertedValue, out error))
                {
                    if (error != null)
                    {
                        throw error;
                    }
    
                    // Leaf nodes are always reinitialized
                    return convertedValue;
                }
    
                if (config != null && config.GetChildren().Any())
                {
                    // 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);
                        if (instance != null)
                        {
                            return instance;
                        }
    
                        instance = CreateInstance(type);
                    }
    
                    // See if its a Dictionary
                    var collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type);
                    if (collectionInterface != null)
                    {
                        BindDictionary(instance, collectionInterface, config, options);
                    }
                    else if (type.IsArray)
                    {
                        instance = BindArray((Array)instance, config, options);
                    }
                    else
                    {
                        // See if its an ICollection
                        collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type);
                        if (collectionInterface != null)
                        {
                            BindCollection(instance, collectionInterface, config, options);
                        }
                        // Something else
                        else
                        {
                            BindNonScalar(config, instance, options);
                        }
                    }
                }
    
                return instance;
            }
    
            private static object CreateInstance(Type type)
            {
                var typeInfo = type.GetTypeInfo();
    
                if (typeInfo.IsInterface || typeInfo.IsAbstract)
                {
                    throw new InvalidOperationException(Resources.FormatError_CannotActivateAbstractOrInterface(type));
                }
    
                if (type.IsArray)
                {
                    if (typeInfo.GetArrayRank() > 1)
                    {
                        throw new InvalidOperationException(Resources.FormatError_UnsupportedMultidimensionalArray(type));
                    }
    
                    return Array.CreateInstance(typeInfo.GetElementType(), 0);
                }
    
                var hasDefaultConstructor = typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0);
                if (!hasDefaultConstructor)
                {
                    throw new InvalidOperationException(Resources.FormatError_MissingParameterlessConstructor(type));
                }
    
                try
                {
                    return Activator.CreateInstance(type);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException(Resources.FormatError_FailedToActivate(type), ex);
                }
            }
    
            private static void BindDictionary(object dictionary, Type dictionaryType, IConfiguration config, BinderOptions options)
            {
                var typeInfo = dictionaryType.GetTypeInfo();
    
                // IDictionary<K,V> is guaranteed to have exactly two parameters
                var keyType = typeInfo.GenericTypeArguments[0];
                var valueType = typeInfo.GenericTypeArguments[1];
                var keyTypeIsEnum = keyType.GetTypeInfo().IsEnum;
    
                if (keyType != typeof(string) && !keyTypeIsEnum)
                {
                    // We only support string and enum keys
                    return;
                }
    
                var setter = typeInfo.GetDeclaredProperty("Item");
                foreach (var child in config.GetChildren())
                {
                    var item = BindInstance(
                        type: valueType,
                        instance: null,
                        config: child,
                        options: options);
                    if (item != null)
                    {
                        if (keyType == typeof(string))
                        {
                            var key = child.Key;
                            setter.SetValue(dictionary, item, new object[] { key });
                        }
                        else if (keyTypeIsEnum)
                        {
                            var key = Enum.Parse(keyType, child.Key);
                            setter.SetValue(dictionary, item, new object[] { key });
                        }
                    }
                }
            }
    
            private static void BindCollection(object collection, Type collectionType, IConfiguration config, BinderOptions options)
            {
                var typeInfo = collectionType.GetTypeInfo();
    
                // ICollection<T> is guaranteed to have exactly one parameter
                var itemType = typeInfo.GenericTypeArguments[0];
                var addMethod = typeInfo.GetDeclaredMethod("Add");
    
                foreach (var section in config.GetChildren())
                {
                    try
                    {
                        var item = BindInstance(
                            type: itemType,
                            instance: null,
                            config: section,
                            options: options);
                        if (item != null)
                        {
                            addMethod.Invoke(collection, new[] { item });
                        }
                    }
                    catch
                    {
                    }
                }
            }
    
            private static Array BindArray(Array source, IConfiguration config, BinderOptions options)
            {
                var children = config.GetChildren().ToArray();
                var arrayLength = source.Length;
                var elementType = source.GetType().GetElementType();
                var newArray = Array.CreateInstance(elementType, arrayLength + children.Length);
    
                // binding to array has to preserve already initialized arrays with values
                if (arrayLength > 0)
                {
                    Array.Copy(source, newArray, arrayLength);
                }
    
                for (int i = 0; i < children.Length; i++)
                {
                    try
                    {
                        var item = BindInstance(
                            type: elementType,
                            instance: null,
                            config: children[i],
                            options: options);
                        if (item != null)
                        {
                            newArray.SetValue(item, arrayLength + i);
                        }
                    }
                    catch
                    {
                    }
                }
    
                return newArray;
            }
    
            private static bool TryConvertValue(Type type, string value, string path, out object result, out Exception error)
            {
                error = null;
                result = null;
                if (type == typeof(object))
                {
                    result = value;
                    return true;
                }
    
                if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    if (string.IsNullOrEmpty(value))
                    {
                        return true;
                    }
                    return TryConvertValue(Nullable.GetUnderlyingType(type), value, path, 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(path, type), ex);
                    }
                    return true;
                }
    
                return false;
            }
    
            private static object ConvertValue(Type type, string value, string path)
            {
                object result;
                Exception error;
                TryConvertValue(type, value, path, out result, out error);
                if (error != null)
                {
                    throw error;
                }
                return result;
            }
    
            private static Type FindOpenGenericInterface(Type expected, Type actual)
            {
                var actualTypeInfo = actual.GetTypeInfo();
                if(actualTypeInfo.IsGenericType &&
                    actual.GetGenericTypeDefinition() == expected)
                {
                    return actual;
                }
    
                var interfaces = actualTypeInfo.ImplementedInterfaces;
                foreach (var interfaceType in interfaces)
                {
                    if (interfaceType.GetTypeInfo().IsGenericType &&
                        interfaceType.GetGenericTypeDefinition() == expected)
                    {
                        return interfaceType;
                    }
                }
                return null;
            }
    
            private static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type)
            {
                var allProperties = new List<PropertyInfo>();
    
                do
                {
                    allProperties.AddRange(type.DeclaredProperties);
                    type = type.BaseType.GetTypeInfo();
                }
                while (type != typeof(object).GetTypeInfo());
    
                return allProperties;
            }
        }

    1、绑定简单类型

      如果IConfiguration为IConfigurationSection类型,且Value不为null

      如果绑定的类型为object,则直接返回Value

      如果绑定类型为Nullable<>,则返回对应的类型

      获取绑定的类型的TypeConverter,如果可以和字符串转换,则返回

    2、如果IConfiguration为IConfigurationSection类型且GetChildren()有任意值

      绑定集合:

      如果类型实现IReadOnlyList<>,IReadOnlyCollection<>,ICollection<>,IEnumerable<>

      绑定字典:

      如果类型实现IReadOnlyDictionary,Dictionary<,>

      

  • 相关阅读:
    phpmyadmin详细的图文使用教程
    从入门到深入FIDDLER 2
    TestNG学习-001-基础理论知识
    TestNG学习-002-annotaton 注解概述及其执行顺序
    自动化测试如何解决验证码的问题
    自动化测试 -- 通过Cookie跳过登录验证码
    JMeter学习-012-JMeter 配置元件之-HTTP Cookie管理器-实现 Cookie 登录
    自动化测试框架
    并发和并行概念及原理
    匿名内部实现多线程的两种方式创建
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12538910.html
Copyright © 2020-2023  润新知