3

I have classes to read in a custom config file, which work in my (sandbox) MVC app, and in a console app. However, it fails to read anything except the top level section when it's run from the web forms app that actually needs it. They all target framework .Net 4.5. The custom config is a 'carrier' section that contains a list of carriers, each of which has their own settings. The web.config extracts:

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="CarrierSection" type="SmallWFApp.CarrierSection"/>
    </configSections>

The custom config section file:

<?xml version="1.0"?>
<CarrierSection>
  <carriers>
    <carrier name="ups" default-code="UPS0041" >
      <pickupreq value="N"/>
      <limits>
        <limit name="non-doc" weight="200" length="270" width="165" height="165" square="330" />
        <limit name="doc" weight="2.5"  length="0" width="0" height="0" square="0" />
        <limit name="consignment" max-eu="200" max-world="0"/>
      </limits>
      <services>
        <service name="document" caption="DOCUMENT EXPRESS" code="D" live="Y"/>
        <service name="parcel" caption="EXPRESS" code="N" live="Y" />
        <service name = "road" caption="ECONOMY ROAD" code="P" live="N" />
      </services>
    </carrier>
    <carrier name="dhl" default-code="DHL0014" >
      <pickupreq value="Y"/>
      <limits >
        <limit name="non-doc" weight="200" length="270" width="165" height="165" square="330"  />
        <limit name="doc"  weight="2.5"  length="0" width="0" height="0" square="0"/>
      </limits>
      <services>
        <service name="9am" caption="NEXT DAY 9AM"  code="9" />
        <service name = "noon" caption="NEXT DAY 12NOON"  code="2" />
      </services>
    </carrier>
  </carriers>
</CarrierSection>

The c# config section classes that (I think) provide the strong typing:

using System;
using System.Configuration;

namespace SmallWFApp
{
public class CarrierSection : ConfigurationSection
{
    [ConfigurationProperty("carriers")]
    public CarrierElementCollection Carriers
    {
        get { return this["carriers"] as CarrierElementCollection; }
    }
}

//-----------------------------------------

public class CarrierElementCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new CarrierElement();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((CarrierElement)element).Name;
    }
    public override ConfigurationElementCollectionType CollectionType
    {
        get { return ConfigurationElementCollectionType.BasicMap; }
    }
    protected override string ElementName
    {
        get { return "carrier"; }
    }
    public CarrierElement this[int index]
    {
        get { return (CarrierElement)BaseGet(index); }
        set
        {
            if (BaseGet(index) != null)
            {
                BaseRemoveAt(index);
            }
            BaseAdd(index, value);
        }
    }
    new public CarrierElement this[string employeeID]
    {
        get { return (CarrierElement)BaseGet(employeeID); }
    }
    public bool ContainsKey(string key)
    {
        bool result = false;
        object[] keys = BaseGetAllKeys();
        foreach (object obj in keys)
        {
            if ((string)obj == key)
            {
                result = true;
                break;
            }
        }
        return result;
    }
}

//-----------------------------------------------------------------
public class CarrierElement : ConfigurationElement
{
    [ConfigurationProperty("name", DefaultValue = "Other", IsRequired = true, IsKey = true)]
     public string Name
    {
        get { return this["name"] as string; }
        set  { this["name"] = value;  }
    }


    [ConfigurationProperty("default-code", IsRequired = true)]
    public string DefaultCode
    {
        get  { return this["default-code"] as string;  }
        set  {  this["default-code"] = value;  }
    }

    [ConfigurationProperty("pickupreq")]
    public ReqPickupConfigElement ReqPickup
    {
        get  {  return base["pickupreq"] as ReqPickupConfigElement;  }
    }


    [ConfigurationProperty("limits")]
    public LimitElementCollection Limits
    {
        get  { return this["limits"] as LimitElementCollection; }
    }

    [ConfigurationProperty("services")]
    public ServiceElementCollection Services
    {
        get  { return this["services"] as ServiceElementCollection;  }
    }
}


//--------------------------------------------

public class ReqPickupConfigElement : System.Configuration.ConfigurationElement
{
    [ConfigurationProperty("value", DefaultValue = "N")]
    [StringValidator(MinLength = 1, MaxLength = 1)]

    public string Value
    {
        get   {  return base["value"] as string;  }
    }
}

The code in the code-behind-aspx.cs class that reads it:

try
{
    CarrierSection config =
                        (CarrierSection)System.Configuration.ConfigurationManager.GetSection(
                        "CarrierSection");

    if (config != null)
    {
        foreach (CarrierElement app in config.Carriers)
        {
            //... (never comes in here) ...
        }

        //lbConfigread.Text = found.ToString();
    }
}
catch (Exception ex)
{
    //...
}

The c# configuration collection/elements to read the 'limits' for each carrier:

namespace SmallWFApp
{
    public class LimitElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new LimitElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((LimitElement)element).Name;
        }
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
        protected override string ElementName
        {
            get { return "limit"; }
        }
        public LimitElement this[int index]
        {
            get { return (LimitElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }
        new public LimitElement this[string employeeID]
        {
            get { return (LimitElement)BaseGet(employeeID); }
        }
        public bool ContainsKey(string key)
        {
            bool result = false;
            object[] keys = BaseGetAllKeys();
            foreach (object obj in keys)
            {
                if ((string)obj == key)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }
    }
    public class LimitElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
        public string Name
        {
            get { return this["name"] as string; }
            set  {  this["name"] = value;  }
        }

        [ConfigurationProperty("weight", IsRequired = false)]
        public string Weight
        {
            get  {  return this["weight"] as string;  }
            set {  this["weight"] = value; }
        }

        [ConfigurationProperty("length", IsRequired = false)]
        public string Length
        {
            get  {  return this["length"] as string; }
            set  { this["length"] = value;  }
        }
        [ConfigurationProperty("width", IsRequired = false)]
        public string Width
        {
            get  { return this["width"] as string; }
            set  { this["width"] = value;  }
        }
        [ConfigurationProperty("height", IsRequired = false)]
        public string Height
        {
            get  { return this["height"] as string; }
            set { this["height"] = value;  }
        }
        [ConfigurationProperty("square", IsRequired = false)]
        public string Square
        {
            get {  return this["square"] as string;  }
            set  { this["square"] = value; }
        }

        // for total consignment

        [ConfigurationProperty("max-eu", IsRequired = false)]
        public string MaxEU
        {
            get  {  return this["max-eu"] as string;  }
            set  {  this["max-eu"] = value;  }
        }
        [ConfigurationProperty("max-world", IsRequired = false)]
        public string MaxWorld
        {
            get  { return this["max-world"] as string; }
            set  { this["max-world"] = value;  }
        }
    }
}

The c# configuration collection/elements to read the 'service' for each carrier:

namespace SmallWFApp
{
    public class ServiceElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new ServiceElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ServiceElement)element).Name;
        }
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
        protected override string ElementName
        {
            get { return "service"; }
        }
        public ServiceElement this[int index]
        {
            get { return (ServiceElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }
        new public ServiceElement this[string employeeID]
        {
            get { return (ServiceElement)BaseGet(employeeID); }
        }
        public bool ContainsKey(string key)
        {
            bool result = false;
            object[] keys = BaseGetAllKeys();
            foreach (object obj in keys)
            {
                if ((string)obj == key)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }
    }
    public class ServiceElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
        public string Name
        {
            get {  return this["name"] as string; }
            set  { this["name"] = value;  }
        }

        [ConfigurationProperty("caption", IsRequired = true)]
        public string Caption
        {
            get  { return this["caption"] as string; }
            set  { this["caption"] = value;  }
        }

        [ConfigurationProperty("code", IsRequired = true)]
        public string Code
        {
            get  { return this["code"] as string;  }
            set { this["code"] = value;  }
        }

        [ConfigurationProperty("live", IsRequired = false)]
        public string Live
        {
            get  {  return this["live"] as string;  }
            set  {  this["live"] = value; }
        }
    }
}

So when you run it, there is no error, but when you examine the variable in the debugger, the collection of carrier property is null:

?config.Carriers
Count = 0
    AddElementName: "add"
    ClearElementName: "clear"
    CollectionType: BasicMap
    Count: 0
    CurrentConfiguration: null
    ElementInformation: {System.Configuration.ElementInformation}
    ElementName: "carrier"
    ElementProperty: {System.Configuration.ConfigurationElementProperty}
    ...
    Properties: {System.Configuration.ConfigurationPropertyCollection}
    ...

If this contains some obvious faux-pas then I'm sorry, but it's driving me to distraction; I need to have released this fix some days ago!

krlzlx
  • 5,752
  • 14
  • 47
  • 55
kerry_hood
  • 33
  • 4
  • Where are defined `limits`, `limit` and `services`, `service`? If I remove these nodes in the config I get 2 Carriers. – krlzlx Jan 23 '17 at 15:32
  • Thanks for looking! I'd removed them in an attempt to be more readable, However, if I replace them, it still has no carrier collection being returned, So, I've restored them in my question, and would be grateful if you could take another look? – kerry_hood Jan 23 '17 at 15:53
  • I'm sorry, I couldn't reproduce your problem. I got 2 Carriers even with the limits and services nodes. Is your `CarrierSection` in the Web.config or in a separate configuration file? – krlzlx Jan 23 '17 at 16:07
  • Yes, the `CarrierSection` is in another file, `parcelcarriers.config`. It's properties include Build Action: Content and Copy to the output directory: always. – kerry_hood Jan 23 '17 at 16:11
  • I had forgotten to put `` in the web.config file, after recompiling it worked! – kerry_hood Jan 23 '17 at 16:16
  • You posted this comment when I posted my answer! – krlzlx Jan 23 '17 at 16:17
  • YAY it works, thank you so much, thought it would be a simple error. – kerry_hood Jan 23 '17 at 16:20
  • You're welcome! Always glad to help. I removed the solved from the title and the solution from the question. If somebody see your question, he will no understand where is the problem if the solution is in your code. And the `Solved` is not needed in the title as users can see that there's a validated answer. – krlzlx Jan 23 '17 at 16:34

1 Answers1

2

You have to specify in your Web.config file where is located your custom configuration file parcelcarriers.config:

<CarrierSection configSource="parcelcarriers.config"/>
krlzlx
  • 5,752
  • 14
  • 47
  • 55