public async Task <SectionView> GetSectionAsync(string name)
        {
            var section = await db.Sections.FirstOrDefaultAsync(x => x.Name.ToLower() == name.ToLower());

            var sectionView = new SectionView(section);

            var configItemViews = new Dictionary <string, ConfigItemView>();

            sectionView.Enums = new Dictionary <string, string[]>();

            if (sectionTypes.SectionServerTypes.TryGetValue(section.Type, out Type sectionServerType))
            {
                AddFields(sectionServerType);
            }
            if (sectionTypes.SectionClientTypes.TryGetValue(section.Type, out Type sectionClientType))
            {
                AddFields(sectionClientType);
            }

            void AddFields(Type sectionType)
            {
                var dataObject = JsonSerializer.Deserialize(section.Options, sectionType);

                foreach (var propertyInfo in sectionType.GetProperties())
                {
                    ConfigItemAttribute configItemAttribute = propertyInfo.GetCustomAttribute <ConfigItemAttribute>();
                    var configItemView = new ConfigItemView
                    {
                        Name = propertyInfo.Name,
                        Type = configItemAttribute.ConfigItemType.Name.Split(".")[^ 1].Replace("Item", "")
Exemplo n.º 2
0
        public static void SectionProcess(Section section, SectionTypes sectionTypes, ICheckRoles checkRoles)
        {
            section.Options = section.Options?.MakeJsonTextNotNull();

            if (checkRoles != null)
            {
                section.Roles = checkRoles.CheckAndSetRoles(section.Roles);
            }

            var options = new Dictionary <string, object>();

            if (sectionTypes.SectionServerTypes.TryGetValue(section.Type, out Type sectionServerType))
            {
                AddFields(section.Options, sectionServerType, options);
            }
            if (sectionTypes.SectionClientTypes.TryGetValue(section.Type, out Type sectionClientType))
            {
                AddFields(section.Options, sectionClientType, options);
            }

            section.Options = JsonSerializer.Serialize(options);

            void AddFields(string optionsJson, Type sectionType, Dictionary <string, object> optionsToAdd)
            {
                var dataObject = JsonSerializer.Deserialize(optionsJson, sectionType);
                var properties = sectionType.GetProperties();

                foreach (var propertyInfo in properties)
                {
                    ConfigItemAttribute configItemAttribute = propertyInfo.GetCustomAttribute <ConfigItemAttribute>();

                    string name     = propertyInfo.Name;
                    string typeName = configItemAttribute.ConfigItemType.Name.Split(".")[^ 1].Replace("Item", "");
Exemplo n.º 3
0
        static void Main(string[] args)
        {
            StringBuilder descriptionsBuilder = new StringBuilder(@"Configuration
*************

");

            StringBuilder exampleBuilder = new StringBuilder(@"Sample configuration (mainnet)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

    [
");

            foreach (string assemblyName in _assemblyNames)
            {
                Assembly assembly = Assembly.Load(new AssemblyName(assemblyName));
                foreach (Type configType in assembly.GetTypes().Where(t => typeof(IConfig).IsAssignableFrom(t)).Where(t => !t.IsInterface))
                {
                    descriptionsBuilder.Append($@"{configType.Name}
{string.Empty.PadLeft(configType.Name.Length, '^')}

");

                    exampleBuilder.AppendLine("      {");
                    exampleBuilder.AppendLine($"        \"ConfigModule\": \"{configType.Name}\"");
                    exampleBuilder.AppendLine("        \"ConfigItems\": {");

                    var properties = configType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    foreach (PropertyInfo propertyInfo in properties)
                    {
                        exampleBuilder.AppendLine($"          \"{propertyInfo.Name}\" : example");
                        ConfigItemAttribute attribute = propertyInfo.GetCustomAttribute <ConfigItemAttribute>();
                        if (attribute == null)
                        {
                            descriptionsBuilder.AppendLine($" - {propertyInfo.Name} - description missing").AppendLine();
                            continue;
                        }

                        descriptionsBuilder.AppendLine($" - {propertyInfo.Name} - {attribute.Description}").AppendLine();
                    }

                    exampleBuilder.AppendLine("        }");
                    exampleBuilder.AppendLine("      },");
                }
            }

            exampleBuilder.AppendLine("    ]");

            string result = string.Concat(descriptionsBuilder.ToString(), exampleBuilder.ToString());

            Console.WriteLine(result);
            Console.ReadLine();
            File.WriteAllText("configuration.rst", result);
        }
Exemplo n.º 4
0
        private static void CheckDefault(PropertyInfo property, object instance)
        {
            ConfigItemAttribute attribute = property.GetCustomAttribute <ConfigItemAttribute>();

            if (attribute == null)
            {
                //there are properties without attribute - we don't pay attention to them
                return;
            }

            string expectedValue = attribute.DefaultValue?.Trim('"') ?? "null";
            string actualValue;

            object value = property.GetValue(instance);

            if (value == null)
            {
                actualValue = "null";
            }
            else if (value is bool)
            {
                actualValue = value.ToString()?.ToLowerInvariant();
            }
            else if (value is IList actualValueArray)
            {
                // there is a case when we have default value as [4, 8, 8] and we need to compare this string to int[] so removing brackets and whitespaces
                string[] expectedItems = expectedValue
                                         .Trim('[').Trim(']')
                                         .Replace(" ", "")
                                         .Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);


                int length = Math.Min(expectedItems.Length, actualValueArray.Count);
                for (int i = 0; i < length; i++)
                {
                    string?actualValueAtIndex   = actualValueArray[i]?.ToString();
                    string expectedValueAtIndex = expectedItems[i];
                    Assert.AreEqual(actualValueAtIndex, expectedValueAtIndex,
                                    $"Property: {property.Name}, expected value at index {i}: <{expectedValueAtIndex}> but was <{actualValueAtIndex}>");
                }

                Assert.AreEqual(actualValueArray.Count, expectedItems.Length,
                                $"Property: {property.Name}, expected value length: <{expectedItems.Length}> but was <{actualValueArray.Count}>");

                return;
            }
            else
            {
                actualValue = value.ToString();
            }

            Assert.AreEqual(actualValue, expectedValue,
                            $"Property: {property.Name}, expected value: <{expectedValue}> but was <{actualValue}>");
        }
Exemplo n.º 5
0
        public void All_default_values_are_correct(Type configType)
        {
            ConfigProvider configProvider = new ConfigProvider();

            PropertyInfo[] properties = configType.GetProperties();

            Type   implementationType = configType.Assembly.GetTypes().SingleOrDefault(t => t.IsClass && configType.IsAssignableFrom(t));
            object instance           = Activator.CreateInstance(implementationType);


            foreach (PropertyInfo property in properties)
            {
                ConfigItemAttribute attribute = property.GetCustomAttribute <ConfigItemAttribute>();
                if (attribute == null)
                {
                    //there are properties without attribute - we don't pay attention to them
                    continue;
                }

                string expectedValue = attribute.DefaultValue?.Trim('"') ?? "null";
                string actualValue;

                object value = property.GetValue(instance);
                if (value == null)
                {
                    actualValue = "null";
                }
                else if (value is bool)
                {
                    actualValue = value.ToString().ToLowerInvariant();
                }
                else if (value is int[])
                {
                    //there is a case when we have default value as [4, 8, 8] and we need to compare this string to int[] so removing brackets and whitespaces
                    int[] items = (int[])value;
                    expectedValue = expectedValue.Trim('[').Trim(']');
                    expectedValue = expectedValue.Replace(" ", "");
                    string[] numbers = expectedValue.Split(',');

                    for (int i = 0; i < numbers.Length; i++)
                    {
                        Assert.AreEqual(items[i].ToString(), numbers[i]);
                    }
                    continue;
                }
                else
                {
                    actualValue = value.ToString();
                }

                Assert.AreEqual(expectedValue, actualValue, $"Property: {property.Name}, expected value: <{expectedValue}> but was <{actualValue}>");
            }
        }
Exemplo n.º 6
0
        private static void CheckDescribedOrHidden(PropertyInfo property, object instance)
        {
            ConfigItemAttribute attribute = property.GetCustomAttribute <ConfigItemAttribute>();

            if (string.IsNullOrWhiteSpace(attribute?.Description) && !(attribute?.HiddenFromDocs ?? false))
            {
                ConfigCategoryAttribute categoryLevel = property.DeclaringType?.GetCustomAttribute <ConfigCategoryAttribute>();
                if (!(categoryLevel?.HiddenFromDocs ?? false))
                {
                    throw new AssertionException(
                              $"Config {instance?.GetType().Name}.{property.Name} has no description and is in the docs.");
                }
            }
        }
Exemplo n.º 7
0
        private static void CheckDefault(PropertyInfo property, object instance)
        {
            ConfigItemAttribute attribute = property.GetCustomAttribute <ConfigItemAttribute>();

            if (attribute == null)
            {
                //there are properties without attribute - we don't pay attention to them
                return;
            }

            string expectedValue = attribute.DefaultValue?.Trim('"') ?? "null";
            string actualValue;

            object value = property.GetValue(instance);

            if (value == null)
            {
                actualValue = "null";
            }
            else if (value is bool)
            {
                actualValue = value.ToString().ToLowerInvariant();
            }
            else if (value is int[])
            {
                //there is a case when we have default value as [4, 8, 8] and we need to compare this string to int[] so removing brackets and whitespaces
                int[] items = (int[])value;
                expectedValue = expectedValue.Trim('[').Trim(']');
                expectedValue = expectedValue.Replace(" ", "");
                string[] numbers = expectedValue.Split(',');

                for (int i = 0; i < numbers.Length; i++)
                {
                    Assert.AreEqual(items[i].ToString(), numbers[i]);
                }

                return;
            }
            else
            {
                actualValue = value.ToString();
            }

            Assert.AreEqual(expectedValue, actualValue,
                            $"Property: {property.Name}, expected value: <{expectedValue}> but was <{actualValue}>");
        }
Exemplo n.º 8
0
        public void Generate()
        {
            StringBuilder descriptionsBuilder = new StringBuilder(@"Configuration
*************

Use '/' as the path separator so the configs can be shared between all platforms supported (Linux, Windows, MacOS).
'--config', '--baseDbPath', and '--log' options are available from the command line to select config file, base DB directory prefix and log level respectively. 

");

            StringBuilder exampleBuilder = new StringBuilder(@"Sample configuration (mainnet)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

    [
");

            List <(Type ConfigType, Type ConfigInterface)> configTypes = new List <(Type, Type)>();

            foreach (string assemblyName in _assemblyNames)
            {
                Assembly assembly = Assembly.Load(new AssemblyName(assemblyName));
                foreach (Type type in assembly.GetTypes().Where(t => typeof(IConfig).IsAssignableFrom(t)).Where(t => !t.IsInterface))
                {
                    var configInterface = type.GetInterfaces().Single(i => i != typeof(IConfig));
                    configTypes.Add((type, configInterface));
                }
            }

            foreach ((Type configType, Type configInterface) in configTypes.OrderBy(t => t.ConfigType.Name))
            {
                descriptionsBuilder.Append($@"{configType.Name}
{string.Empty.PadLeft(configType.Name.Length, '^')}

");

                ConfigCategoryAttribute categoryAttribute = configInterface.GetCustomAttribute <ConfigCategoryAttribute>();
                if (categoryAttribute != null)
                {
                    descriptionsBuilder.AppendLine($"{categoryAttribute.Description}").AppendLine();
                }

                exampleBuilder.AppendLine("      {");
                exampleBuilder.AppendLine($"        \"ConfigModule\": \"{configType.Name}\"");
                exampleBuilder.AppendLine("        \"ConfigItems\": {");

                var properties = configType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo propertyInfo in properties.OrderBy(p => p.Name))
                {
                    PropertyInfo interfaceProperty = configInterface.GetProperty(propertyInfo.Name);
                    if (interfaceProperty == null)
                    {
                        Console.WriteLine($"Property {propertyInfo.Name} is missing from interface {configInterface.Name}.");
                    }

                    ConfigItemAttribute attribute = interfaceProperty.GetCustomAttribute <ConfigItemAttribute>();
                    string defaultValue           = attribute == null ? "[MISSING_DOCS]" : attribute.DefaultValue;

                    exampleBuilder.AppendLine($"          \"{propertyInfo.Name}\" : {defaultValue},");

                    if (attribute == null)
                    {
                        descriptionsBuilder.AppendLine($" {propertyInfo.Name}").AppendLine();
                        continue;
                    }

                    descriptionsBuilder
                    .AppendLine($" {propertyInfo.Name}")
                    .AppendLine($"   {attribute.Description}")
                    .AppendLine($"   default value: {defaultValue}")
                    .AppendLine();
                }

                if (exampleBuilder.Length > 0 && exampleBuilder[exampleBuilder.Length - 1] == ',')
                {
                    exampleBuilder.Remove(exampleBuilder.Length - 1, 1);
                }

                exampleBuilder.AppendLine("        }");
                exampleBuilder.AppendLine("      },");
            }

            exampleBuilder.AppendLine("    ]");

            string result = string.Concat(descriptionsBuilder.ToString(), exampleBuilder.ToString());

            Console.WriteLine(result);
            File.WriteAllText("configuration.rst", result);
            File.WriteAllText("../../../docs/source/configuration.rst", result);
        }
Exemplo n.º 9
0
        private object GetTypeConfigItemValue(ConfigItem configItem, Type valType, object defVal, List <string> pathItemNames)
        {
            if (configItem == null)
            {
                LocalLoggingService.Warning("配置项为NULL,valType:{0},pathItemNames:{1}", valType, string.Join(",", pathItemNames.ToArray()));
                return(defVal);
            }
            if (ConfigHelper.IsUnderlyingType(valType))
            {
                return(ConfigHelper.ChangeType(configItem.Value, valType, defVal));
            }
            if (typeof(IList).IsAssignableFrom(valType))
            {
                var genericArguments = valType.GetGenericArguments();
                if (genericArguments.Length != 1 || !valType.IsGenericType)
                {
                    return(null);
                }
                Type         listItemType     = genericArguments.First();
                IList        list             = (IList)Activator.CreateInstance(valType);
                ConfigItem[] childConfigItems = InternalGetChildConfigItems(configItem.AppName, pathItemNames.ToArray());
                if (childConfigItems == null || childConfigItems.Length == 0)
                {
                    return(list);
                }
                foreach (ConfigItem childItem in childConfigItems)
                {
                    listItemType = listItemType != typeof(object) ? listItemType : Type.GetType(childItem.ValueType);
                    List <string> childPathItemNames = new List <string>(pathItemNames)
                    {
                        childItem.Name
                    };
                    if (listItemType == null)
                    {
                        LocalLoggingService.Warning("listItemType 为NULL,childItem.ValueType:{0},pathItemNames:{1}", childItem.ValueType, string.Join(",", childPathItemNames.ToArray()));
                        return(defVal);
                    }
                    object itemValue = GetTypeConfigItemValue(childItem, listItemType, listItemType == typeof(string) ? null : Activator.CreateInstance(listItemType), childPathItemNames);
                    list.Add(itemValue);
                }
                return(list);
            }
            else if (typeof(IDictionary).IsAssignableFrom(valType))
            {
                var genericArguments = valType.GetGenericArguments();
                if (genericArguments.Length != 2 || genericArguments.First() != typeof(string) || !valType.IsGenericType)
                {
                    return(null);
                }
                Type         dicItemTypeOfValue = null;
                IDictionary  dic = (IDictionary)Activator.CreateInstance(valType);
                ConfigItem[] childConfigItems = InternalGetChildConfigItems(configItem.AppName, pathItemNames.ToArray());
                if (childConfigItems == null || childConfigItems.Length == 0)
                {
                    return(dic);
                }
                foreach (ConfigItem childItem in childConfigItems)
                {
                    dicItemTypeOfValue = genericArguments.Last();
                    dicItemTypeOfValue = dicItemTypeOfValue != typeof(object) ? dicItemTypeOfValue : Type.GetType(childItem.ValueType);
                    List <string> childPathItemNames = new List <string>(pathItemNames)
                    {
                        childItem.Name
                    };
                    if (dicItemTypeOfValue == null)
                    {
                        LocalLoggingService.Warning("dicItemTypeOfValue 为NULL,childItem.ValueType:{0},pathItemNames:{1}", childItem.ValueType, string.Join(",", childPathItemNames.ToArray()));
                        return(defVal);
                    }
                    object itemValue = GetTypeConfigItemValue(childItem, dicItemTypeOfValue, dicItemTypeOfValue == typeof(string) ? null : Activator.CreateInstance(dicItemTypeOfValue), childPathItemNames);
                    dic.Add(childItem.Name, itemValue);
                }
                return(dic);
            }
            else
            {
                FieldInfo[] fis = valType.GetFields();
                foreach (FieldInfo fi in fis)
                {
                    if (valType == fi.FieldType)
                    {
                        continue;
                    }
                    List <string> childPathItemNames = new List <string>(pathItemNames)
                    {
                        fi.Name
                    };
                    ConfigItem childItem = InternalGetConfigItem(configItem.AppName, childPathItemNames.ToArray());
                    if (childItem == null)
                    {
                        ConfigItemAttribute configItemAttribute = fi.GetCustomAttributes(false).OfType <ConfigItemAttribute>().SingleOrDefault();
                        if (configItemAttribute == null)
                        {
                            configItemAttribute = new ConfigItemAttribute();
                        }
                        string friendlyName = configItemAttribute.FriendlyName ?? fi.Name;
                        string desc         = configItemAttribute.Description;
                        string value        = null;
                        if (ConfigHelper.IsUnderlyingType(fi.FieldType))
                        {
                            if (fi.FieldType.IsEnum)
                            {
                                EnsureEnumTypeAdded(null, fi.FieldType);
                            }
                            value = fi.GetValue(defVal) != null ? (fi.GetValue(defVal)).ToString() : null;
                        }
                        childItem = InternalAddConfigItem(configItem.AppName, pathItemNames.ToArray(), fi.Name, friendlyName, desc, value, GetConfigItemSourceId(fi.FieldType), ConfigHelper.GetConfigItemValueType(fi.FieldType), ConfigHelper.GetConfigItemValueTypeEnum(fi.FieldType), ConfigHelper.IsCompositeValue(fi.FieldType));
                    }
                    object itemValue = GetTypeConfigItemValue(childItem, fi.FieldType, ConfigHelper.IsUnderlyingType(fi.FieldType) ? fi.GetValue(defVal) : fi.GetValue(defVal) ?? Activator.CreateInstance(fi.FieldType), childPathItemNames);
                    fi.SetValue(defVal, itemValue);
                }

                PropertyInfo[] pis = valType.GetProperties();
                foreach (PropertyInfo pi in pis)
                {
                    if (valType == pi.PropertyType)
                    {
                        continue;
                    }
                    List <string> childPathItemNames = new List <string>(pathItemNames)
                    {
                        pi.Name
                    };
                    ConfigItem childItem = InternalGetConfigItem(configItem.AppName, childPathItemNames.ToArray());
                    if (childItem == null)
                    {
                        ConfigItemAttribute configItemAttribute = pi.GetCustomAttributes(false).OfType <ConfigItemAttribute>().SingleOrDefault();
                        if (configItemAttribute == null)
                        {
                            configItemAttribute = new ConfigItemAttribute();
                        }
                        string friendlyName = configItemAttribute.FriendlyName ?? pi.Name;
                        string desc         = configItemAttribute.Description;
                        string value        = null;
                        if (ConfigHelper.IsUnderlyingType(pi.PropertyType))
                        {
                            if (pi.PropertyType.IsEnum)
                            {
                                EnsureEnumTypeAdded(null, pi.PropertyType);
                            }
                            value = pi.GetValue(defVal, null) != null ? (pi.GetValue(defVal, null)).ToString() : null;
                        }
                        childItem = InternalAddConfigItem(configItem.AppName, pathItemNames.ToArray(), pi.Name, friendlyName, desc, value, GetConfigItemSourceId(pi.PropertyType), ConfigHelper.GetConfigItemValueType(pi.PropertyType), ConfigHelper.GetConfigItemValueTypeEnum(pi.PropertyType), ConfigHelper.IsCompositeValue(pi.PropertyType));
                    }
                    if (pi.CanWrite)
                    {
                        object itemValue = GetTypeConfigItemValue(childItem, pi.PropertyType, ConfigHelper.IsUnderlyingType(pi.PropertyType) ? pi.GetValue(defVal, null) : pi.GetValue(defVal, null) ?? Activator.CreateInstance(pi.PropertyType), childPathItemNames);
                        pi.SetValue(defVal, itemValue, null);
                    }
                }
            }
            return(defVal);
        }
Exemplo n.º 10
0
        private ConfigItem AddTypeConfigItem(bool isTopLevel, string appName, List <string> parentPathItemNames, string name, string friendlyName, string desc, Type valType, object defVal)
        {
            ConfigItemAttribute   configItemAttribute;
            ConfigEntityAttribute configEntityAttribute;
            string value = null;

            if (ConfigHelper.IsUnderlyingType(valType))
            {
                value = defVal != null?defVal.ToString() : null;
            }
            else if (isTopLevel)
            {
                configEntityAttribute = valType.GetCustomAttributes(false).OfType <ConfigEntityAttribute>().SingleOrDefault();
                if (configEntityAttribute == null)
                {
                    configEntityAttribute = new ConfigEntityAttribute();
                }
                friendlyName = configEntityAttribute.FriendlyName ?? name;
                desc         = configEntityAttribute.Description;
            }
            List <string> childPathItemnames = new List <string>(parentPathItemNames)
            {
                name
            };
            ConfigItem configItem = InternalGetConfigItem(appName, childPathItemnames.ToArray());

            if (configItem == null)
            {
                configItem = InternalAddConfigItem(appName, parentPathItemNames.ToArray(), name, friendlyName, desc, value, GetConfigItemSourceId(valType), ConfigHelper.GetConfigItemValueType(valType), ConfigHelper.GetConfigItemValueTypeEnum(valType), ConfigHelper.IsCompositeValue(valType));
            }
            if (ConfigHelper.IsUnderlyingType(valType))
            {
                if (valType.IsEnum)
                {
                    EnsureEnumTypeAdded(null, valType);
                }
                return(configItem);
            }
            List <string> pathItemNames = new List <string>(parentPathItemNames)
            {
                configItem.Name
            };

            if (typeof(IList).IsAssignableFrom(valType))
            {
                var genericArguments = valType.GetGenericArguments();
                if (genericArguments.Length != 1 || !valType.IsGenericType)
                {
                    return(configItem);
                }
                Type  listItemType = genericArguments.First();
                int   listIndex    = 0;
                IList defListVal   = (IList)defVal;
                if (defListVal != null && !configItem.ItemsInited)
                {
                    foreach (object listItem in defListVal)
                    {
                        listItemType = listItem.GetType();
                        name         = string.Format("{0}_{1}", configItem.Name, listIndex++);
                        AddTypeConfigItem(false, appName, pathItemNames, name, name, null, listItemType, listItem);
                    }
                    ConfigServer.SetItemsInited(appName, pathItemNames.ToArray());
                }
            }
            else if (typeof(IDictionary).IsAssignableFrom(valType))
            {
                var genericArguments = valType.GetGenericArguments();
                if (genericArguments.Length != 2 || genericArguments.First() != typeof(string) || !valType.IsGenericType)
                {
                    return(configItem);
                }
                Type        dicItemTypeOfValue = genericArguments.Last();
                IDictionary defDicVal          = (IDictionary)defVal;
                if (defDicVal != null && !configItem.ItemsInited)
                {
                    foreach (DictionaryEntry dicItem in defDicVal)
                    {
                        dicItemTypeOfValue = dicItem.Value.GetType();
                        name = string.Format("{0}", dicItem.Key);
                        AddTypeConfigItem(false, appName, pathItemNames, name, name, null, dicItemTypeOfValue, dicItem.Value);
                    }
                    ConfigServer.SetItemsInited(appName, pathItemNames.ToArray());
                }
            }
            else
            {
                FieldInfo[] fis = valType.GetFields();
                foreach (FieldInfo fi in fis)
                {
                    if (valType == fi.FieldType)
                    {
                        continue;
                    }
                    configItemAttribute = fi.GetCustomAttributes(false).OfType <ConfigItemAttribute>().SingleOrDefault();
                    if (configItemAttribute == null)
                    {
                        configItemAttribute = new ConfigItemAttribute();
                    }
                    friendlyName = configItemAttribute.FriendlyName ?? fi.Name;
                    desc         = configItemAttribute.Description;
                    AddTypeConfigItem(false, appName, pathItemNames, fi.Name, friendlyName, desc, fi.FieldType, ConfigHelper.IsUnderlyingType(fi.FieldType) ? fi.GetValue(defVal) : fi.GetValue(defVal) ?? Activator.CreateInstance(fi.FieldType));
                }

                PropertyInfo[] pis = valType.GetProperties();
                foreach (PropertyInfo pi in pis)
                {
                    if (valType == pi.PropertyType)
                    {
                        continue;
                    }
                    configItemAttribute = pi.GetCustomAttributes(false).OfType <ConfigItemAttribute>().SingleOrDefault();
                    if (configItemAttribute == null)
                    {
                        configItemAttribute = new ConfigItemAttribute();
                    }
                    friendlyName = configItemAttribute.FriendlyName ?? pi.Name;
                    desc         = configItemAttribute.Description;
                    AddTypeConfigItem(false, appName, pathItemNames, pi.Name, friendlyName, desc, pi.PropertyType, ConfigHelper.IsUnderlyingType(pi.PropertyType) ? pi.GetValue(defVal, null) : pi.GetValue(defVal, null) ?? Activator.CreateInstance(pi.PropertyType));
                }
            }
            return(configItem);
        }