views:

804

answers:

7

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.

A: 

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.

Tom Anderson
going to add code samples, sorry
Tom Anderson
+1  A: 

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.

Øyvind Skaar
[citation needed] :P - informative, but can you edit a link in there, and mention which version this would be?
annakata
It is something they call config transformations, which is a part of the new MSDeploy. I am not sure if it is web only. It was unveiled at PDC as a part of the web futures. It is mentioned here: http://blogs.msdn.com/webdevtools/archive/2008/12/30/vs-2010-for-web-developer-previews.aspx
Øyvind Skaar
+2  A: 

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.

Gavin Miller
+4  A: 

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.

Xian
Once I got the post-build copy line done, this worked great. I haven't tested it deployed, but in local debug it works.I'm a little disappointed that Visual Studio doesn't do this already in a easier fashion.
James
Yeah definitely true... I also try and go one step further and configure as much as possible in code alone, trying to achieve an environment "self-aware" application based on such things as IPAddress or machine name. But that's another story entirely.
Xian
A: 

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.

mykhaylo
you need a space before the #if for it to work right on here, otherwise it bolds.
Tom Anderson
A: 

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.

sfuqua
+1  A: 

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.

Ian