Get the path to the config
This bit turned out to be very easy:
you just use something like this:
public string FileLocation
{
get { return Configuration.FilePath; }
}
private static Configuration Configuration
{
get { return OpenConfig(ConfigurationUserLevel.PerUserRoamingAndLocal); }
}
private static Configuration OpenConfig(ConfigurationUserLevel userLevel)
{
return ConfigurationManager.OpenExeConfiguration(userLevel);
}
There are two other values for the enum passed in there: None seems to point to where the app.config would be and PerUserRoaming always seems to point to somewhere that doesnt exist. I couldnt find out any more details about when this may be appropriate to use but in my case it appears not to be.
As just touched on above one of the confusing things about this call is that it will return the path of the file should one be saved/exist. This means that often this file wont exist! The unclear relationship between the ConfigurationManager and Settings (the autogenerated class derived from ApplicationSettingsBase) is what caused me a lot of pain!
save a blank config
A few stumbling blocks were as follows:
if you add a settings.Settings class this will also add an app.config. Unless you ship this with your installed program then you will see different behaviour between the installed version of the program and the dev version - one will create settings files using values from the app.config and the other wont. The simplest solution for me was to delete the app.config altogether.
I was trying to use the save method on Configuration which lets you specify that you save all properties (regardless of value being default) and lets you specify to save even if the configuration has not been modified. However calling this method with both flags set wont do anything - it appears that the settings you create in the designer in some way arent real until they get set - Configuration and ConfigurationManager are the older way of doing things and while ApplicationSettingsBase probably uses one or both of them they dont integrate nicely.
To make it more confusing, if you query the settings then they will have values (for strings they will have ""). However if you save the settings then these settings aren't saved to the location specified in the file! In fact no file will be created at all! Although if you have an app.config a file will be created if you use the force save method above as the defaults are specified in the app.config!
The solution appears to be to check for the default value in each of the properties and then set the default value. This looks like it should be a no-op but the getter doesnt follow the logic of 'if I dont have a setting I'll create it when queried' instead it just pretends one exists with the default value. Setting any value seems to actually create the setting.
var somethingUnSet = _settings.Something == "";
if (somethingUnset)
_settings.Something = "";
if(somethingUnset|| somethingElseUnset)
{
_settings.Save();
ExitWithError("config needs setting up at " + _settings.FileLocation);
return;
}
Finally this gave me the desired result - a file gets saved in the location specified by config manager with the empty properties that need filling in. Think a UI would have been quicker!!!
I was using a wrapped Settings object to facilitate changing values in tests without having to deal with a singleton. This also has the nice side effect that you can tack on extra code (like the file location) and not have to worry about the code gen overwriting stuff when you add more settings.