0
votes

I need to create a simple customized ConfigurationSection with an IEnumerable inside it.

I have read several articles and stackoverflow links, taking this as a simple example: How to create custom config section in app.config?

So, I have this config file section inside a Console App:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="Disk"
             type="ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection"/>
  </configSections>
  <Disk>
    <Paths>
      <Path name="one" permission="1" />
      <Path name="two" permission="2" />
      <Path name="three" permission="3" />
    </Paths>
  </Disk>
</configuration>

Next I have this whole structure to manage the config section: using System.Configuration;

namespace ConsoleApplication1_ConfigurationEnumerable
{
    public class Path: ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get
            {
                return (string)this["name"];
            }
        }

        [ConfigurationProperty("permission", IsRequired=true)]
        public string Permission
        {
            get
            {
                return (string)this["permission"];
            }
        }
    }    

    public class Paths: ConfigurationElementCollection 
    {
        public Path this[int index] 
        {
            get
            {
                return base.BaseGet(index) as Path;
            }
            set 
            {
                if (base.BaseGet(index) != null) {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        protected override ConfigurationElement CreateNewElement() 
        {
            return new Path();
        }
        protected override object  GetElementKey(ConfigurationElement element)
        {
            return ((Path)element).Name;
        }
    }

    public class PathsConfigSection : ConfigurationSection
    {
        public static PathsConfigSection GetConfig()
        {
            //return (PathsConfigSection)System.Configuration.ConfigurationManager.GetSection("Disk") ?? new PathsConfigSection();
            return (PathsConfigSection)System.Configuration.ConfigurationManager.GetSection("Paths") ?? new PathsConfigSection();
        }

        [ConfigurationProperty("Paths")]
        public Paths Paths
        {
            get 
            {
                object o = this["Paths"];
                return o as Paths;
            }
        }
    }
}

And here the program.cs using the whole thing: using System;

namespace ConsoleApplication1_ConfigurationEnumerable
{
    class Program
    {
        static void Main(string[] args)
        {
            var config = PathsConfigSection.GetConfig();
            if (config == null || config.Paths.Count == 0)
            {
                Console.WriteLine("Is null or empty");
            }
            else
            {
                foreach (Path item in config.Paths)
                {
                    Console.WriteLine("Item {0} with valuer {1}", item.Name, item.Permission);
                }
            }
        }
    }
}

The problem here is inside this two lines:

//return (PathsConfigSection)System.Configuration
//       .ConfigurationManager.GetSection("Disk") ?? new PathsConfigSection();
return (PathsConfigSection)System.Configuration
       .ConfigurationManager.GetSection("Paths") ?? new PathsConfigSection();

If I use the second one (uncommented above) it returns null.

If I use the commented one, then it throws an exception like this:

System.Configuration.ConfigurationErrorsException was unhandled
HResult=-2146232062 Message=An error occurred creating the configuration section handler for Disk: Could not load type 'ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. (C:\Users\blackberry\Desktop\ConsoleApplication1_ConfigurationEnumerable\ConsoleApplication1_ConfigurationEnumerable\bin\Debug\ConsoleApplication1_ConfigurationEnumerable.vshost.exe.config line 4) Source=System.Configuration BareMessage=An error occurred creating the configuration section handler for Disk: Could not load type 'ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Filename=C:\Users\blackberry\Desktop\ConsoleApplication1_ConfigurationEnumerable\ConsoleApplication1_ConfigurationEnumerable\bin\Debug\ConsoleApplication1_ConfigurationEnumerable.vshost.exe.config Line=4 StackTrace: at System.Configuration.BaseConfigurationRecord.FindAndEnsureFactoryRecord(String configKey, Boolean& isRootDeclaredHere) at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject) at System.Configuration.BaseConfigurationRecord.GetSection(String configKey) at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName) at System.Configuration.ConfigurationManager.GetSection(String sectionName) at ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection.GetConfig() in C:\Users\blackberry\Desktop\ConsoleApplication1_ConfigurationEnumerable\ConsoleApplication1_ConfigurationEnumerable\Disk.cs:line 63 at ConsoleApplication1_ConfigurationEnumerable.Program.Main(String[] args) in C:\Users\blackberry\Desktop\ConsoleApplication1_ConfigurationEnumerable\ConsoleApplication1_ConfigurationEnumerable\Program.cs:line 9 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: System.TypeLoadException HResult=-2146233054 Message=Could not load type 'ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Source=System.Configuration TypeName=ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection StackTrace: at System.Configuration.TypeUtil.GetTypeWithReflectionPermission(IInternalConfigHost host, String typeString, Boolean throwOnError) at System.Configuration.RuntimeConfigurationRecord.RuntimeConfigurationFactory.Init(RuntimeConfigurationRecord configRecord, FactoryRecord factoryRecord) at System.Configuration.RuntimeConfigurationRecord.RuntimeConfigurationFactory.InitWithRestrictedPermissions(RuntimeConfigurationRecord configRecord, FactoryRecord factoryRecord) at System.Configuration.RuntimeConfigurationRecord.CreateSectionFactory(FactoryRecord factoryRecord) at System.Configuration.BaseConfigurationRecord.FindAndEnsureFactoryRecord(String configKey, Boolean& isRootDeclaredHere) InnerException:

Where is my fault ?

2

2 Answers

4
votes

You need to use the Fully Qualified Assembly Name when specifying the class in the configSections tag:

<configSections>
  <section name="Disk"
           type="ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection, ConsoleApplication1"/>
</configSections>

This assumes that the name of your assembly is ConsoleApplication1. If you are still getting exceptions, you can determine the correct value using the the following code:

typeof(ConsoleApplication1_ConfigurationEnumerable.PathsConfigSection).FullName
                                                                      .ToString()

BTW: Your namespace is odd. Naming standard suggest that you use the dot (.) when separating namespace hierarchies:

namespace ConsoleApplication1.ConfigurationEnumerable
0
votes

I think you should use the first line (the commented-out one). The problem is in your <section> element in the config file. Follow Daniel's answer to see how that should look.