I have a web service that needs different settings for different environments (debug, test, prod). What's the easiest way to setup separate config files for these different environments? Everything I find on the web tells me how to use configuration manager to retrieve settings, but not how to find particular settings based on the current build configuration.
We use a few different methods.
Environment.MachineName.config (for users)
System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap();
if (System.IO.File.Exists(String.Format("./{0}.config", Environment.MachineName)))
fileMap.ExeConfigFilename = String.Format(@"./{0}.config", Environment.MachineName);
else
fileMap.ExeConfigFilename = "live.config";
System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None);
#IF DEBUG debug.config
#IF TEST test.config
#IF PROD prod.config
System.Configuration.ExeConfigurationFileMap fileMap = new System.Configuration.ExeConfigurationFileMap();
#if (DEBUG)
fileMap.ExeConfigFilename = "./debug.config";
#elif (TEST)
fileMap.ExeConfigFilename = "./test.config";
#else
fileMap.ExeConfigFilename = "./production.config";
#endif
System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, System.Configuration.ConfigurationUserLevel.None);
It can get tedious though.
In the next version of Visual Studio this very thing has been announced as a new feature. Perhaps you can do it in a prebuild script; replacing the config file based on the configurationname env variable.
One way would be to maintain 3 different configuration files and choose them via MSBuild when deploying.
<Choose>
<When Condition="$(BuildEnvironment) == 'debug'">
<PropertyGroup>
<ConfigFile>debug.config</ConfigFile>
</PropertyGroup>
</When>
<When Condition="$(BuildEnvironment) == 'test'">
<PropertyGroup>
<ConfigFile>test.config</ConfigFile>
</PropertyGroup>
</When>
<When Condition="$(BuildEnvironment) == 'prod'">
<PropertyGroup>
<ConfigFile>prod.config</ConfigFile>
</PropertyGroup>
</When>
</Choose>
By utilizing an MSBuild task you can rename and push the specific configuration file to the proper location.
Still somewhat cumbersome, this has the added advantage of moving towards a one step build.
I find having several config files for each environment works well. ie:
- config\local.endpoints.xml
- config\ dev.endpoints.xml
- config\ test.endpoints.xml
- config\ staging.endpoints.xml
- config\ prod.endpoints.xml
I then link to a "master" version of this using the built in configSource attribute within the web.config or app.config such as
<appSettings configSource="config\endpoints.xml"/>
I would then use the build process or deploy process to copy the the correct configuration for the environment down to the name that the web.config is expecting.
Each environment is clearly labelled and controlled, without the need of messy placeholders.
Use precompiled directives. Sample code:
String configFile = String.Empty;
#if Debug
configFile = @"debug.config";
#elif Test
configFile = @"test.config";
#elif Prod
configFile = @"prod.config";
#endif
Load(configFile);
Where Load method loaded config file.
Yet another option: switch to storing your application configuration in a database. I keep the type values in a database, so that management of these settings is more centralized. Then my config file has a connection string that is dedicated just for Config, and it has just two keys: a unique application ID value, and the config version (i.e. "dev", "test", etc.). Then I just deploy the right config file to the right environment.
That may not be exactly what you're looking for; it is an alternative for facilitating management of your config data.
The issue I have with Xian's answer is that if you add a new config setting/endpoint you have to remember to do it across several files. If you forget to do so then you'll only find out when you deploy to the affected environment (not good).
Instead you could have one master config and using regex/xmlpoke(nant)/[your-favorite-text-manipulator] to massage the file at build/deploy time to insert the right values for each environment, keeping the settings for all environments in another file (but crucially all together).
Then you only have to maintain the one config file and a file of environment settings - I think this makes maintenance easier and clearer in the long-term.