Search code examples
c#windowsapplication-settings

Array of structures in Application Settings


What's a clean, canonical way to save a series of identically-structured simple config tuples in .NET Application Settings?

Say my program's got (user-scope) settings like "server name", "address", "port". I'd just add those three to the Application Settings as two strings and an int.
But what if my program's config needs to deal with multiple such servers, where the user can add and remove any number of them? I would want the settings to hold an array of triplets, each triplet made of two strings and an int, and the Application Settings don't quite seem to support that.

It's a relatively lightweight structure made of basic types, but so far the cleanest way I've found to do this is to create a new class(!) just to hold the array of triplets, fit the class with custom serialization/deserialization code, and then set it as the type of a single "setting".
I could do this, but considering how hands-off most of the Application Settings is, this really smells like something that's probably doable in a more straightforward way.

So is there such a way?

(Yes, I've seen the "int[] array in application Settings" question, and the useful answers from there are not that useful in this situation.)

ETA: The point of the question isn't in server names or port numbers, it's in an array of identically-structured tuples of simple data, where each individual datum would be trivially saved.


Solution

  • You might consider having a custom string for the value portion, one which you can parse (similar to a connection string), and then require a key name that begins with a constant, like ServerString. Then you can have a method in your code that reads all the keys that begin with this constant and parse the value portion into a Tuple.

    For example, consider the app.config values:

      <appSettings>
        <add key="ServerString1" value="name=MyServerName;address=127.0.0.1;port=80" />
        <add key="ServerString2" value="name=MyServerName;address=127.0.0.2;port=81" />
        <add key="ServerString3" value="name=MyServerName;address=127.0.0.3;port=82" />
      </appSettings>
    

    And consider a method that knows how to parse a valid value string into a Tuple<string, string, int>:

    private static Tuple<string, string, int> GetTupleFromServerSetting(string serverSetting)
    {
        if (serverSetting == null) throw new ArgumentNullException("serverSetting");
        var settings = serverSetting.Split(';');
    
        if (settings.Length != 3) 
            throw new ArgumentException("One or more server settings is missing.", 
            "serverSetting");
    
        var name = string.Empty;
        var address = string.Empty;
        int port = 0;
    
        foreach (var setting in settings)
        {
            var settingParts = setting.Split('=');
    
            if (settingParts.Length != 2)
                throw new ArgumentException(string.Format(
                    "Setting is missing a value: {0}", setting),
                    "serverSetting");
    
            switch (settingParts[0].ToLower())
            {
                case "name":
                    name = settingParts[1];
                    break;
                case "address":
                    address = settingParts[1];
                    break;
                case "port":
                    port = int.Parse(settingParts[1]);
                    break;
            }                
        }
    
        return new Tuple<string, string, int>(name, address, port);
    }
    

    Then, when you're parsing your app.config, you could do something like this to get a list of all the server settings:

    List<Tuple<string, string, int>> serverSettings =
        ConfigurationManager.AppSettings.AllKeys.Where(key =>
            key.StartsWith("ServerString", StringComparison.OrdinalIgnoreCase))
            .Select(key => GetTupleFromServerSetting(ConfigurationManager.AppSettings[key]))
            .ToList();