Tuesday, December 18, 2018

.NET Generic Configuration Section

If we need to extend an app.config file of .net application with a deep level configuration section like this
  
  <DevicesConfiguration>
    <devices>
      <device type="SM_G920I###6.0.1">
        <touchActivities>
          <touchActivity type="ToggleFlightMode">
            <points>
              <point x="500" y="1800" id="1"/>
              <point x="1300" y="430" id="2"/>
            </points>
          </touchActivity>
          <touchActivity type="ToggleVOLTE">
            <points>
              <point x="500" y="2400" id="1"/>
              <point x="1300" y="1000" id="2"/>
            </points>
          </touchActivity>
        </touchActivities>
      </device>
      <device type="SM_J510FN###7.1.1">
        <touchActivities>
          <touchActivity type="ToggleFlightMode">
            <points>
              <point x="340" y="220" id="1"/>
              <point x="340" y="610" id="2"/>
              <point x="640" y="200" id="3"/>
            </points>
          </touchActivity>
          <touchActivity type="Enable3G">
            <points>
              <point x="340" y="220" id="1"/>
              <point x="360" y="960" id="2"/>
              <point x="370" y="390" id="3"/>
              <point x="270" y="580" id="4"/>
            </points>
          </touchActivity>
        </touchActivities>
      </device>
    </devices>
  </DevicesConfiguration>
we have to create at least 3 classes inherited from ConfigurationElementCollection to incapsulate 'devices', 'touchActivities' and 'points' sections. It could be too much code unless we create generic ConfigurationElementCollection and use it everywhere instead of creation separate classes.
The following code depicts that generic implementation:

  
    /// 
    /// ConfigurationElement with additional Id property
    /// 
    public abstract class IdentifiableConfigurationElement : ConfigurationElement
    {
        public virtual string Id { get; }
    }

    /// 
    /// Generic ConfigurationElementCollection
    /// 
    /// 
    public class ConfigurationElementCollection<T> : ConfigurationElementCollection where T : IdentifiableConfigurationElement, new()
    {
        public T this[int index]
        {
            get { return (T)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }

        public void Add(T serviceConfig)
        {
            BaseAdd(serviceConfig);
        }

        public void Clear()
        {
            BaseClear();
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new T();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((T)element).Id;
        }

        public void Remove(T serviceConfig)
        {
            BaseRemove(serviceConfig.Id);
        }

        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }

        public void Remove(String name)
        {
            BaseRemove(name);
        }
    }
Additional Id property is necessary for implementation GetElementKey(..) method.
And now all the configuration section code looks pretty compact:

  
    /// 
    /// Main configuration section
    /// 
    public class DevicesConfigurationSection : ConfigurationSection
    {
        [ConfigurationProperty("devices")]
        [ConfigurationCollection(typeof(DeviceConfigElement), AddItemName = "device", CollectionType = ConfigurationElementCollectionType.BasicMap)]
        public ConfigurationElementCollection<DeviceConfigElement> Devices => base["devices"] as ConfigurationElementCollection<DeviceConfigElement>;
    }
    

    public class DeviceConfigElement : IdentifiableConfigurationElement
    {
        public override string Id => Type;

        [ConfigurationProperty("type")]
        public string Type => base["type"] as string;

        [ConfigurationProperty("touchActivities")]
        [ConfigurationCollection(typeof(TouchActivityConfigElement), AddItemName = "touchActivity", CollectionType = ConfigurationElementCollectionType.BasicMap)]
        public ConfigurationElementCollection<TouchActivityConfigElement> TouchActivities => base["touchActivities"] as ConfigurationElementCollection<TouchActivityConfigElement>;
    }


    public class TouchActivityConfigElement : IdentifiableConfigurationElement
    {
        public override string Id => Type.ToString();

        [ConfigurationProperty("type")]
        public string Type => base["type"].ToString();

        [ConfigurationProperty("points")]
        [ConfigurationCollection(typeof(PointConfigElement), AddItemName = "point", CollectionType = ConfigurationElementCollectionType.BasicMap)]
        public ConfigurationElementCollection<PointConfigElement> Points => base["points"] as ConfigurationElementCollection<PointConfigElement>;
    }
    

    public class PointConfigElement : IdentifiableConfigurationElement
    {
        [ConfigurationProperty("x")]
        public int X => (int)base["x"];

        [ConfigurationProperty("y")]
        public int Y => (int)base["y"];

        [ConfigurationProperty("id")]
        public override string Id => base["id"].ToString();
    }


That's it! Don't forget to declare the section in the config file
  
  <configSections>
    <section name="DevicesConfiguration" type="MyNamespace.DevicesConfigurationSection, MyNamespace" />
  </configSections>

And use if from C# code:
  
  var devicesConfiguration = System.Configuration.ConfigurationManager.GetSection("DevicesConfiguration") as DevicesConfigurationSection;

No comments:

Post a Comment