This blog post might look like any other standard blog posts, answering question, which can be googled and found on stackoverflow. But it isn’t. You see… it composes couple of interesting things you might need for you custom configuration. Also it is not congested with explanations. I’m adding this as quick reference for myself, so I don’t spend my time googling a lot to find answers. Also if you just starting with custom configuration and don’t want to read MSDN pages, please refer to my earlier blog post on basics here.

Let’s get back to topic:

We want section in our app/web.config with collection which will be able to contain elements without ugly “add” tag and also have CDATA inside. See configuration:

    <Feeds defaultPollingInterval="00:10:00">
      <Feed>
        <![CDATA[http://www.andriybuday.com/getXmlFeed.aspx?someParam=A&somethingElse=B]]>
      </Feed>
      <Feed pollingInterval="00:05:00">
        <![CDATA[http://www.andriybuday.com/getXmlFeed.aspx?someParam=C&somethingElse=D]]>
      </Feed>
    </Feeds>

So as you can see in collection of elements there is custom name “Feed”, which is awesome. Also notice that URL contains weird characters (not for us, but for XML), so we surround URL into CDATA. Those feeds are fake of course.

To make all this happen we need few things:

  1. Override CollectionType property for our collection, and set type to BasicMap
  2. Override ElementName property for our collection, and return preferred name
  3. Override DeserializeElement method for element inside collection. Here you need to manually fetch your attributes, like I do for poollingInterval and read contents of CDATA. Please refer to source code below to see how this is done as it is bit tricky. For example because of the nature of the XmlReader you need to read attributes first and then proceed to contents.

Source code below (interesting pieces are in bold):

[ConfigurationCollection(typeof(FeedConfigElement))]
public class FeedsConfigElementCollection : ConfigurationElementCollection
{
    [ConfigurationProperty("defaultPollingInterval", DefaultValue = "00:10:00")]
    public string DefaultPollingInterval
    {
        get
        {
            return (string)base["defaultPollingInterval"];
        }
    }
    protected override ConfigurationElement CreateNewElement()
    {
        return new FeedConfigElement();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((FeedConfigElement)(element)).Url;
    }

    // In order to avoid standard keyword "add"
    // we override ElementName and set CollectionType to BasicMap
    protected override string ElementName
    {
        get { return "Feed"; }
    }

    public override ConfigurationElementCollectionType CollectionType
    {
        get { return ConfigurationElementCollectionType.BasicMap; }
    }
    public FeedConfigElement this[int index]
    {
        get
        {
            return (FeedConfigElement)BaseGet(index);
        }
    }
}

public class FeedConfigElement : ConfigurationElement
{
    public string Url { get; private set; }

    public string PollingInterval { get; private set; }

    // To get value from the CDATA we need to overrride this method
    protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
    {
        PollingInterval = reader.GetAttribute("pollingInterval") ?? "00:00:00";

        // Also for some unknown reason for CDATA ReadElementContentAsString returns 
        // a lot of spaces before and after the actual string, so we Trim it
        Url = reader.ReadElementContentAsString().Trim();
    }
}

Hope this gives quick answers to some of you. It took me good portion of time to find all this things, because for some odd reason it wasn’t so much easy to find.

Some links: