beefsupreme
beefsupreme

Reputation: 125

Loading Properties.Settings from a different file at runtime

Is there any way to load settings from a different file other than the default App.config file at runtime? I'd like to do this after the default config file is loaded.

I use the Settings.Settings GUI in Visual Studio to create my App.config file for me. The config file ends up looking like this:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <configSections>
            <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
        <section name="SnipetTester.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
    </configSections>
      <applicationSettings>
        <SnipetTester.Properties.Settings>
          <setting name="SettingSomething" serializeAs="String">
            <value>1234</value>
          </setting>
        </SnipetTester.Properties.Settings>
      </applicationSettings>
    </configuration>

In code, I'm able to access the settings like this:

Console.WriteLine("Default setting value:  " + Properties.Settings.Default.SettingSomething);

The idea is that when the application is run, I should be able to specify a config file at run time and have the application load the config file into the Properties.Settings.Default object instead of using the default app.config file. The formats of the config files would be the same, but the values of the settings would be different.

I know of a way to do this with the ConfigurationManager.OpenExeConfiguration(configFile);. However, in the tests that I've run, it doesn't update the Properties.Settings.Default object to reflect the new values from the config file.


After thinking about this a bit longer, I've been able to come up with a solution that I like a little better. I'm sure it has some pitfalls, but I think it'll work for what I need it to do.

Essentially, the Properties.Settings class is automatically generated by Visual Studio; it generates the code for the class for you. I was able to find where the code was generated and add a few function calls to load a config file on its own. Here's my addition:

internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 
{
    //Parses a config file and loads its settings
    public void Load(string filename)
    {
        System.Xml.Linq.XElement xml = null;
        try
        {
            string text = System.IO.File.ReadAllText(filename);
            xml = System.Xml.Linq.XElement.Parse(text);
        }
        catch
        {
            //Pokemon catch statement (gotta catch 'em all)

            //If some exception occurs while loading the file,
            //assume either the file was unable to be read or
            //the config file is not in the right format.
            //The xml variable will be null and none of the
            //settings will be loaded.
        }

        if(xml != null)
        {
            foreach(System.Xml.Linq.XElement currentElement in xml.Elements())
            {
                switch (currentElement.Name.LocalName)
                {
                    case "userSettings":
                    case "applicationSettings":
                        foreach (System.Xml.Linq.XElement settingNamespace in currentElement.Elements())
                        {
                            if (settingNamespace.Name.LocalName == "SnipetTester.Properties.Settings")
                            {
                                foreach (System.Xml.Linq.XElement setting in settingNamespace.Elements())
                                {
                                    LoadSetting(setting);
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    //Loads a setting based on it's xml representation in the config file
    private void LoadSetting(System.Xml.Linq.XElement setting)
    {
        string name = null, type = null, value = null;

        if (setting.Name.LocalName == "setting")
        {
            System.Xml.Linq.XAttribute xName = setting.Attribute("name");
            if (xName != null)
            {
                name = xName.Value;
            }

            System.Xml.Linq.XAttribute xSerialize = setting.Attribute("serializeAs");
            if (xSerialize != null)
            {
                type = xSerialize.Value;
            }

            System.Xml.Linq.XElement xValue = setting.Element("value");
            if (xValue != null)
            {
                value = xValue.Value;
            }
        }


        if (string.IsNullOrEmpty(name) == false &&
            string.IsNullOrEmpty(type) == false &&
            string.IsNullOrEmpty(value) == false)
        {
            switch (name)
            {
                //One of the pitfalls is that everytime you add a new
                //setting to the config file, you will need to add another
                //case to the switch statement.
                case "SettingSomething":
                    this[name] = value;
                    break;
                default:
                    break;
            }
        }
    }
}

The code I added exposes an Properties.Settings.Load(string filename) function. The function accepts a config filename as a parameter. It will parse the file and load up any settings it encounters in the config file. To revert back to the original configuration, simply call Properties.Settings.Reload().

Hope this might help someone else!

Upvotes: 8

Views: 12461

Answers (3)

Akuh Sukiakuh
Akuh Sukiakuh

Reputation: 1

You can include the types so you don't need to manually update the source every time.

`private void LoadSetting(System.Xml.Linq.XElement setting) { string name = null, type = null; string value = null;

        if (setting.Name.LocalName == "setting")
        {
            System.Xml.Linq.XAttribute xName = setting.Attribute("name");
            if (xName != null)
            {
                name = xName.Value;
            }

            System.Xml.Linq.XAttribute xSerialize = setting.Attribute("serializeAs");
            if (xSerialize != null)
            {
                type = xSerialize.Value;
            }

            System.Xml.Linq.XElement xValue = setting.Element("value");
            if (xValue != null)
            {
                if (this[name].GetType() == typeof(System.Collections.Specialized.StringCollection))
                {
                    foreach (string s in xValue.Element("ArrayOfString").Elements())
                    {
                        if (!((System.Collections.Specialized.StringCollection)this[name]).Contains(s))
                            ((System.Collections.Specialized.StringCollection)this[name]).Add(s);
                    }
                }
                else
                {
                    value = xValue.Value;
                }

                if (this[name].GetType() == typeof(int))
                {
                    this[name] = int.Parse(value);
                }
                else if (this[name].GetType() == typeof(bool))
                {
                    this[name] = bool.Parse(value);
                }
                else
                {
                    this[name] = value;
                }

            }
        }`

Upvotes: 0

Bogdan Maxim
Bogdan Maxim

Reputation: 5946

It depends on the type of the application:

  1. Web Application & Windows Application - use the configSource xml attribute if you are willing to store the config files in the same folder (or subfolders) as the application
  2. Create a settings provider and also implement IApplicationSettingsProvider. Samples here and here. You might also need to use the IConfigurationManagerInternal interface to replace the default .NET configuration manager. When implementing the provider don't forget to make a difference between user settings and application settings and the roaming profiles.

If you want to get started quickly just decompile the LocalFileSettingsProvider class (the default settings provider) and change it to your needs (you might find some useles code and might need to replicate all of the classes on which it depends).

Good luck

Upvotes: 1

Look at using ExeConfigurationFileMap and ConfigurationManager.OpenMappedExeConfiguration.

See Cracking the Mysteries of .Net 2.0 Configuration

The ExeConfigurationFileMap allows you to specifically configure the exact pathnames to machine, exe, roaming and local configuration files, all together, or piecemeal, when calling OpenMappedExeConfiguration(). You are not required to specify all files, but all files will be identified and merged when the Configuration object is created. When using OpenMappedExeConfiguration, it is important to understand that all levels of configuration up through the level you request will always be merged. If you specify a custom exe and local configuration file, but do not specify a machine and roaming file, the default machine and roaming files will be found and merged with the specified exe and user files. This can have unexpected consequences if the specified files have not been kept properly in sync with default files.

Upvotes: 1

Related Questions