Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested custom elements in app.config (C#)

G'da to everyone,

For hours I've been trying to figure how to read settings from app.config file:

<?xml version="1.0"?>
<configuration>

  <configSections>
    <section name="Databases" type="McFix.DatabaseSection, McFix"/>
  </configSections>

  <Databases>
    <Database name="database">
      <Tables>
        <Table name="be_sessions">
          <Columns>
            <Column name="sess_id">
            </Column>
          </Columns>
        </Table>
      </Tables>
    </Database>
  </Databases>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

The the code for custom handler classes is here, also copied below:

public class DatabaseSection : ConfigurationSection
{
    [ConfigurationProperty("Databases", IsDefaultCollection = false)]
    public DatabaseInstanceCollection Databases
    {
        get { return (DatabaseInstanceCollection)this["Databases"]; }
        set { this[""] = value; }
    }
}
[ConfigurationCollection(typeof(DatabaseElement), AddItemName = "add", CollectionType = ConfigurationElementCollectionType.BasicMap )]
public class DatabaseInstanceCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new DatabaseElement();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((DatabaseElement)element).Name;
    }
}
public class DatabaseElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
    public string Name
    {
        get { return (string)base["name"]; }
        set { base["name"] = value; }
    }
}

public class TableSection : ConfigurationSection
{
    [ConfigurationProperty("Tables", IsDefaultCollection = true)]
    public TableInstanceCollection Tables
    {
        get { return (TableInstanceCollection)this["Tables"]; }
        set { this[""] = value; }
    }
}

[ConfigurationCollection(typeof(TableElement), AddItemName = "Table", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class TableInstanceCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new TableElement();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((TableElement)element).Name;
    }
}

public class TableElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
    public string Name
    {
        get { return (string)base["name"]; }
        set { base["name"] = value; }
    }
}

public class ColumnSection : ConfigurationSection
{
    [ConfigurationProperty("Columns", IsDefaultCollection = true)]
    public ColumnInstanceCollection Columns
    {
        get { return (ColumnInstanceCollection)this["Columns"]; }
        set { this[""] = value; }
    }
}

[ConfigurationCollection(typeof(ColumnElement), AddItemName = "Column", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class ColumnInstanceCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new ColumnElement();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((ColumnElement)element).Name;
    }
}

public class ColumnElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
    public string Name
    {
        get { return (string)base["name"]; }
        set { base["name"] = value; }
    }
}

The problem is when I attempt to get "Databases" section via GetSection method:

Configuration Config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
DatabaseSection DbConfig = Config.GetSection("Databases") as DatabaseSection;

program throws an ConfigurationErrorsException, reporting "Unrecognized element 'Database'", although it does that after going through get method of DatabaseSection, even though I define AddItemName for DatabaseInstanceCollection as "Database". Am I missing something, attribute that will let underlying code read the app.config correctly?

like image 807
Daniel Protopopov Avatar asked Apr 07 '11 04:04

Daniel Protopopov


2 Answers

Obligatory links:

  • Unraveling the Mysteries of .NET 2.0 Configuration
  • Decoding the Mysteries of .NET 2.0 Configuration
  • Cracking the Mysteries of .NET 2.0 Configuration

After a cursory look it seems like your problem is on this line:

[ConfigurationCollection(typeof(DatabaseElement), AddItemName = "add", CollectionType = ConfigurationElementCollectionType.BasicMap )]

This indicates that the .config file should look like this:

<Databases>
    <add><!-- Database goes here --></add>
</Databases>

I.e. your Databases element is expecting an "add" child, to indicate that an item is to be added to the colleciton.

You should try changing the AddItemName property from "add" to "Database":

[ConfigurationCollection(typeof(DatabaseElement), AddItemName = "Database", CollectionType = ConfigurationElementCollectionType.BasicMap )]

(I've not had a chance to test this, there may be other problems)

like image 83
Justin Avatar answered Nov 11 '22 20:11

Justin


You're correct, Kragen, I had to remove Table/ColumnSection and add Table/ColumnInstanceCollection to Database/TableElement. DatabaseSection' Databases property had to be like:

[ConfigurationProperty("", IsDefaultCollection = true)]
        public DatabaseInstanceCollection Databases
        {
            get { return (DatabaseInstanceCollection)this[""]; }
        }
like image 1
Daniel Protopopov Avatar answered Nov 11 '22 19:11

Daniel Protopopov