I'm playing around with .NET's configuration support (the ConfigurationManager class and related support classes). I would like to write an application that, once installed:
- Has default settings in foo.exe.config (in Program Files).
- The user may later override the settings with nondefault values which should be persisted.
- The user's preferences should be persisted in the user's profile, since he shouldn't have write permissions to the Program Files directory.
The app should use the user's preferences when they're set, otherwise use the defaults.
It seems like this ought to be easy - it's a very common pattern. But my attempts at this are running into bumps and I'm wondering if I'm taking the right approach.
The following code produces the runtime exception "ConfigurationSection properties cannot be edited when locked".
using System;
using System.Configuration;
namespace DemoAppSettingsProblem
{
class Program
{
static void Main(string[] args)
{
Configuration userConfig =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
if ( userConfig.AppSettings.Settings["foo"] != null )
userConfig.AppSettings.Settings.Remove("foo");
userConfig.AppSettings.Settings.Add("foo", "The string is foo.");
userConfig.Save(ConfigurationSaveMode.Modified); // exception!
}
}
}
The problem is that the .NET-defined <appSettings>
section is declared with the default allowExeDefinition=MachineToApplication
(see this nice post by Microsoft's Irena Kennedy). This prohibits the section from being written to the user's profile (either local or roaming).
So, I assume I need to define my own section, with allowExeDefinition=MachineToLocalUser
. But as far as I can tell from the MSDN docs, that means I need to create my own configuration class, derived from ConfigurationSection
. The examples there point me toward more work than I was expecting, which usually sets off my alarm bells that I'm doing something wrong.
Is it really this difficult to achieve this? Is there a simple way .NET provides to support this, or should I perhaps be taking a different approach altogether?