Multiple values for a single config key
Asked Answered
T

10

52

I'm trying to use ConfigurationManager.AppSettings.GetValues() to retrieve multiple configuration values for a single key, but I'm always receiving an array of only the last value. My appsettings.config looks like

<add key="mykey" value="A"/>
<add key="mykey" value="B"/>
<add key="mykey" value="C"/>

and I'm trying to access with

ConfigurationManager.AppSettings.GetValues("mykey");

but I'm only getting { "C" }.

Any ideas on how to solve this?

Tetragon answered 12/5, 2010 at 14:54 Comment(0)
C
56

Try

<add key="mykey" value="A,B,C"/>

And

string[] mykey = ConfigurationManager.AppSettings["mykey"].Split(',');
Craven answered 12/5, 2010 at 14:56 Comment(3)
So what's the point of ConfigurationManager.AppSettings.GetValues() then?Hinkle
@Hinkle one questions the point of the underlying NameValueCollection class - which supports multiple values per key, but doesn't actually let you set more than one per key (AppSettings must internally use the set indexer) - this is the true cause of the issue, rather than GetValues() only returning a single value.Utu
If there is only single value, any character not found error occurs?Hundredweight
C
14

I know I'm late but i found this solution and it works perfectly so I just want to share.

It's all about defining your own ConfigurationElement

namespace Configuration.Helpers
{
    public class ValueElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
        public string Name
        {
            get { return (string) this["name"]; }
        }
    }

    public class ValueElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new ValueElement();
        }


        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ValueElement)element).Name;
        }
    }

    public class MultipleValuesSection : ConfigurationSection
    {
        [ConfigurationProperty("Values")]
        public ValueElementCollection Values
        {
            get { return (ValueElementCollection)this["Values"]; }
        }
    }
}

And in the app.config just use your new section:

<configSections>
    <section name="PreRequest" type="Configuration.Helpers.MultipleValuesSection,
    Configuration.Helpers" requirePermission="false" />
</configSections>

<PreRequest>
    <Values>
        <add name="C++"/>
        <add name="Some Application"/>
    </Values>
</PreRequest>

and when retrieving data just like this :

var section = (MultipleValuesSection) ConfigurationManager.GetSection("PreRequest");
var applications = (from object value in section.Values
                    select ((ValueElement)value).Name)
                    .ToList();

Finally thanks to the author of the original post

Coming answered 7/4, 2015 at 8:29 Comment(0)
U
13

The config file treats each line like an assignment, which is why you're only seeing the last line. When it reads the config, it assigns your key the value of "A", then "B", then "C", and since "C" is the last value, it's the one that sticks.

as @Kevin suggests, the best way to do this is probably a value whose contents are a CSV that you can parse apart.

Unbolted answered 12/5, 2010 at 14:59 Comment(0)
C
6

What you want to do is not possible. You either have to name each key differently, or do something like value="A,B,C" and separate out the different values in code string values = value.split(',').

It will always pick up the value of the key which was last defined (in your example C).

Craze answered 12/5, 2010 at 14:54 Comment(0)
T
5

I use naming convention of the keys and it works like a charm

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="section1" type="System.Configuration.NameValueSectionHandler"/>
  </configSections>
  <section1>
    <add key="keyname1" value="value1"/>
    <add key="keyname21" value="value21"/>
    <add key="keyname22" value="value22"/>
  </section1>
</configuration>

var section1 = ConfigurationManager.GetSection("section1") as NameValueCollection;
for (int i = 0; i < section1.AllKeys.Length; i++)
{
    //if you define the key is unique then use == operator
    if (section1.AllKeys[i] == "keyName1")
    {
        // process keyName1
    }

    // if you define the key as a list, starting with the same name, then use string StartWith function
    if (section1.AllKeys[i].Startwith("keyName2"))
    {
        // AllKeys start with keyName2 will be processed here
    }
}
Trimming answered 24/3, 2017 at 20:58 Comment(2)
it is a good one to have a standard retrieval and by specifying key and valueLengthwise
I think this is the cleanest solution. It also allows for values that may already contain many of the usual separator characters. I implemented it and solved my issue, and I don't have to worry that some day, a value may be required that uses that one particular separator.Remember
A
3

I think, you can use Custom Config Sections https://web.archive.org/web/20211020133931/https://www.4guysfromrolla.com/articles/032807-1.aspx

Allistir answered 1/8, 2012 at 4:40 Comment(0)
P
3

Since the ConfigurationManager.AppSettings.GetValues() method is not working, I've used the following workaround to get a similar effect, but with the need to suffix the keys with unique indexes.

var name = "myKey";
var uniqueKeys = ConfigurationManager.AppSettings.Keys.OfType<string>().Where(
    key => key.StartsWith(name + '[', StringComparison.InvariantCultureIgnoreCase)
);
var values = uniqueKeys.Select(key => ConfigurationManager.AppSettings[key]);

This will match keys like myKey[0] and myKey[1].

Photostat answered 12/11, 2014 at 9:52 Comment(1)
ConfigurationManager.ConnectionStrings gives you the ability to loop through a list which negates any and all answers to this question (I know it's not connection string per say, but you can use it as such)Egocentrism
B
3

Here is full solution: code in aspx.cs

namespace HelloWorld
{
    public partial class _Default : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            UrlRetrieverSection UrlAddresses = (UrlRetrieverSection)ConfigurationManager.GetSection("urlAddresses");
        }
    }

    public class UrlRetrieverSection : ConfigurationSection
    {
        [ConfigurationProperty("", IsDefaultCollection = true,IsRequired =true)]
        public UrlCollection UrlAddresses
        {
            get
            {
                return (UrlCollection)this[""];
            }
            set
            {
                this[""] = value;
            }
        }
    }


    public class UrlCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new UrlElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((UrlElement)element).Name;
        }
    }

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

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

    }
}

And in web config

<configSections>
   <section name="urlAddresses" type="HelloWorld.UrlRetrieverSection" />
</configSections>
<urlAddresses>
    <add name="Google" url="http://www.google.com" />
   <add name="Yahoo"  url="http://www.yahoo.com" />
   <add name="Hotmail" url="http://www.hotmail.com/" />
</urlAddresses>
Bors answered 28/4, 2016 at 12:57 Comment(1)
Thanks CubeJockey for reallignment.Bors
P
3

I found the solution to be very simple. If all keys are going to have the same value, simply use the unique values as the keys and omit the value.

<configSections>
    <section name="appSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
    <section name="filelist" type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
 </configSections>

<filelist>
    <add key="\\C$\Temp\File01.txt"></add>
    <add key="\\C$\Temp\File02.txt"></add>
    <add key="\\C$\Temp\File03.txt"></add>
    <add key="\\C$\Temp\File04.txt"></add>
    <add key="\\C$\Temp\File05.txt"></add>
    <add key="\\C$\Temp\File06.txt"></add>
    <add key="\\C$\Temp\File07.txt"></add>
    <add key="\\C$\Temp\File08.txt"></add>
</filelist>

Then in code simply use the following:

private static List<string> GetSection(string section)
{
    NameValueCollection sectionValues = ConfigurationManager.GetSection(section) as NameValueCollection;
    return sectionValues.AllKeys.ToList();
}

The result is:

enter image description here

Parakeet answered 22/9, 2020 at 19:3 Comment(0)
Y
2

My take on JJS's reply: Config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="List1" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    <section name="List2" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
  <List1>
    <add key="p-Teapot" />
    <add key="p-drongo" />
    <add key="p-heyho" />
    <add key="p-bob" />
    <add key="p-Black Adder" />
  </List1>
  <List2>
    <add key="s-Teapot" />
    <add key="s-drongo" />
    <add key="s-heyho" />
    <add key="s-bob"/>
    <add key="s-Black Adder" />
  </List2>

</configuration>

Code to retrieve into string[]

 private void button1_Click(object sender, EventArgs e)
    {

        string[] output = CollectFromConfig("List1");
        foreach (string key in output) label1.Text += key + Environment.NewLine;
        label1.Text += Environment.NewLine;
        output = CollectFromConfig("List2");
        foreach (string key in output) label1.Text += key + Environment.NewLine;
    }
    private string[] CollectFromConfig(string key)
    {
        NameValueCollection keyCollection = (NameValueCollection)ConfigurationManager.GetSection(key);
        return keyCollection.AllKeys;
    }

IMO, that's as simple as it is going to get. Feel free to prove me wrong :)

Yesima answered 6/3, 2017 at 23:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.