netcore3.0 IConfiguration配置源码解析(三)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了netcore3.0 IConfiguration配置源码解析(三),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含17996字,纯文字阅读大概需要26分钟。
内容图文
![netcore3.0 IConfiguration配置源码解析(三)](/upload/InfoBanner/zyjiaocheng/1241/0b1ae341012c4c3eb8775b41cc33932d.jpg)
前面两篇文章主要讲到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>publicstatic T Get<T>(this IConfiguration configuration, Action<BinderOptions> configureOptions) { if (configuration == null) { thrownew ArgumentNullException(nameof(configuration)); } var result = configuration.Get(typeof(T), configureOptions); if (result == null) { returndefault(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>publicstaticobject 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>publicstaticobject Get(this IConfiguration configuration, Type type, Action<BinderOptions> configureOptions) { if (configuration == null) { thrownew 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>publicstaticvoid 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>publicstaticvoid 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>publicstaticvoid Bind(this IConfiguration configuration, object instance, Action<BinderOptions> configureOptions) { if (configuration == null) { thrownew 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>publicstatic 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>publicstatic 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>publicstaticobject 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>publicstaticobject 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; } privatestaticvoid BindNonScalar(this IConfiguration configuration, object instance, BinderOptions options) { if (instance != null) { foreach (var property in GetAllProperties(instance.GetType().GetTypeInfo())) { BindProperty(property, instance, configuration, options); } } } privatestaticvoid BindProperty(PropertyInfo property, object instance, IConfiguration config, BinderOptions options) { // We don‘t support set only, non public, or indexer propertiesif (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 graphreturn; } propertyValue = BindInstance(property.PropertyType, propertyValue, config.GetSection(property.Name), options); if (propertyValue != null && hasSetter) { property.SetValue(instance, propertyValue); } } privatestaticobject 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 interfacesprivatestaticobject AttemptBindToCollectionInterfaces(Type type, IConfiguration config, BinderOptions options) { var typeInfo = type.GetTypeInfo(); if (!typeInfo.IsInterface) { returnnull; } var collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyList<>), type); if (collectionInterface != null) { // IEnumerable<T> is guaranteed to have exactly one parameterreturn 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 parameterreturn BindToCollection(typeInfo, config, options); } collectionInterface = FindOpenGenericInterface(typeof(ICollection<>), type); if (collectionInterface != null) { // ICollection<T> is guaranteed to have exactly one parameterreturn BindToCollection(typeInfo, config, options); } collectionInterface = FindOpenGenericInterface(typeof(IEnumerable<>), type); if (collectionInterface != null) { // IEnumerable<T> is guaranteed to have exactly one parameterreturn BindToCollection(typeInfo, config, options); } returnnull; } privatestaticobject BindInstance(Type type, object instance, IConfiguration config, BinderOptions options) { // if binding IConfigurationSection, break earlyif (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 reinitializedreturn convertedValue; } if (config != null && config.GetChildren().Any()) { // If we don‘t have an instance, try to create oneif (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 Dictionaryvar collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type); if (collectionInterface != null) { BindDictionary(instance, collectionInterface, config, options); } elseif (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 elseelse { BindNonScalar(config, instance, options); } } } return instance; } privatestaticobject CreateInstance(Type type) { var typeInfo = type.GetTypeInfo(); if (typeInfo.IsInterface || typeInfo.IsAbstract) { thrownew InvalidOperationException(Resources.FormatError_CannotActivateAbstractOrInterface(type)); } if (type.IsArray) { if (typeInfo.GetArrayRank() > 1) { thrownew 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) { thrownew InvalidOperationException(Resources.FormatError_MissingParameterlessConstructor(type)); } try { return Activator.CreateInstance(type); } catch (Exception ex) { thrownew InvalidOperationException(Resources.FormatError_FailedToActivate(type), ex); } } privatestaticvoid BindDictionary(object dictionary, Type dictionaryType, IConfiguration config, BinderOptions options) { var typeInfo = dictionaryType.GetTypeInfo(); // IDictionary<K,V> is guaranteed to have exactly two parametersvar 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 keysreturn; } 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, newobject[] { key }); } elseif (keyTypeIsEnum) { var key = Enum.Parse(keyType, child.Key); setter.SetValue(dictionary, item, newobject[] { key }); } } } } privatestaticvoid BindCollection(object collection, Type collectionType, IConfiguration config, BinderOptions options) { var typeInfo = collectionType.GetTypeInfo(); // ICollection<T> is guaranteed to have exactly one parametervar 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 { } } } privatestatic 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 valuesif (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; } privatestaticbool TryConvertValue(Type type, string value, string path, outobject result, out Exception error) { error = null; result = null; if (type == typeof(object)) { result = value; returntrue; } if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { if (string.IsNullOrEmpty(value)) { returntrue; } 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); } returntrue; } returnfalse; } privatestaticobject 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; } privatestatic 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; } } returnnull; } privatestatic 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<,>
原文:https://www.cnblogs.com/lanpingwang/p/12538910.html
内容总结
以上是互联网集市为您收集整理的netcore3.0 IConfiguration配置源码解析(三)全部内容,希望文章能够帮你解决netcore3.0 IConfiguration配置源码解析(三)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。