Anonymous Configuration Elements

Introduction

Recently, with my XMPP server that is RND I wanted to make an extensible transport system. This would allow the user to build their own transport types (such as BOSH, or hell, even Named Pipes) and wire them into the server. Each type of transport obviously has different configuration options (such as Address and Port for TCP and Name for Named Pipes). This posed an interesting challenge, how do I allow the users to add whatever attributes they want to the configuration?

Configuration data
<connectTo>
  <add target="Transports.TcpTransport, Transports, ..." address="192.168.0.5" port="5222" />
  <add target="Transports.NamedPipeTransport, ..." name="\\.\xmppServer"/>
</connectTo>

So how can we create the functionality without hacking stuff to death?

How I did it

My first clue was the overridable method, OnDeserializeUnrecognizedAttribute(). After overriding it and setting a breakpoint I found it worked perfectly. Now the only trick was to differentiate between declared properties and properties that we have explicitly set.

Luckily the base class didn’t have a wierd implementation, so calling the base.Properties gave me the functionality I needed, so I simply stored those properties in another collection so that I knew which ones where defined dynamically by the user.

The Code

Well, here is the code to do all you have ever needed :), well not really. In any case, it has turned out to be very useful to me.

Anonymous Configuration Elements
    /// <summary>
    /// Represents a configuration element that can support properties not
    /// defined at compile time.
    /// </summary>
    public class AnonymousValueConfigurationElement : ConfigurationElement
    {
        private List<string> _supportedProperties = new List<string>();

        /// <summary>
        /// Gets the list of properties that are unknown.
        /// </summary>
        public IEnumerable<KeyValuePair<string, string>> UnknownProperties
        {
            get
            {
                foreach (ConfigurationProperty prop in Properties)
                {
                    // Only return properties that we don't explicitly support.
                    if (!_supportedProperties.Contains(prop.Name))
                    {
                        yield return new KeyValuePair<string, string>(prop.Name, (string)this[prop.Name]);
                    }
                }
            }
        }

        private ConfigurationPropertyCollection _properties;
        /// <summary>
        /// Gets the combination of all the properties (known and unknown).
        /// </summary>
        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                // Have we done this yet?
                if (_properties == null)
                {
                    // Make our property collection.
                    _properties = new ConfigurationPropertyCollection();

                    ConfigurationPropertyCollection col = base.Properties;
                    foreach (ConfigurationProperty prop in col)
                    {
                        // Add it.
                        _properties.Add(prop);
                        // Add it to our information about the supported properties.
                        _supportedProperties.Add(prop.Name);
                    }
                }
                // Return them.
                return _properties;
            }
        }

        protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
        {
            // Make a new property.
            ConfigurationProperty prop = new ConfigurationProperty(name, typeof(string));
            // Add a new property so that we can set it.
            Properties.Add(prop);
            // Set it.
            this[name] = value;
            // We handled it.
            return true;
        }
    }

It is really simple, and something that maybe should have been included in the framework, but for those who are wondering how to do it; maybe your search is over.

Hope this helps!

This entry was posted in C#, Migrated, Programming. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>