9

Wanted to pick the brains of those MS Build/ VS Post build exponents here.

I would like to have my web.config entries customizable per user/machine/environment.

I could have my configurable/changeable entries marked in the web.config and would like those entries overridden by the respective user/environment file and would like to have an order that decides which entries should trump the other if the entry is found in multiple files.

for eg: web.config has a $connectionstring entry and the customization files per user/environment could have the potential values to replace $connectionstring depending on the context/configuration the solution is built

which means, I could have a set of files like below:

user_joe.config

       $connectionstring = db_where_joe_like_to_connect_to 

staging.config

       $connectionstring = db_where_staging_connect_to  

production.config

       $connectionstring = db_production

so if joe is compiling the solution from his Dev box, the web.config should have the value "db_where_joe_like_to_connect_to" for $connectionstring.

I am hoping there could be a solution that doesn't involve Nant.

hope someone can throw pointers.

thanikkal
  • 3,326
  • 3
  • 27
  • 45

4 Answers4

7

You can use visual studio 2010's web.config transform settings.

http://weblogs.asp.net/gunnarpeipman/archive/2009/06/16/visual-studio-2010-web-config-transforms.aspx

This will allow each developer to have their portion of a web.config that can get merged in for their build settings.

Internally we use an event that was pieced together from various places on the net- since normally this happens during publishing and we wanted it to happen at compile time.

Add a BeforeBuild target So - from the csproj file:

<Target Name="BeforeBuild">
    <TransformXml Source="$(SolutionDir)Web.config" Transform="$(SolutionDir)Web.$(Configuration).config" Destination="$(SolutionDir)Web.$(Configuration).config.transformed" />
  </Target>
  <PropertyGroup>
    <PostBuildEvent>xcopy "$(SolutionDir)Web.$(Configuration).config.transformed" "$(SolutionDir)Web.config" /R /Y</PostBuildEvent>
  </PropertyGroup>


Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71
  • 1
    That's a terrible suggestion. So you're saying that if you have 20 developers, you should create 20 different build targets x Number of build types? That's a maintenance nightmare. – Erik Funkenbusch Sep 20 '11 at 16:56
  • 1
    Ouch. Terrible suggestion or difficult feature? We are open to better suggestions if you have a better way otherwise I'd consider rephrasing to 'that's unfortunate'. If you want this to happen during publishing then it's easy. It's unfortunate though the extra step to happen just for a build. – Adam Tuliper Sep 20 '11 at 18:36
  • Mystere - Also assuming you read the comments below re multiple devs? – Adam Tuliper Sep 20 '11 at 18:46
  • @AdamTuliper - I have a better solution below, that lacks the "unfortunate" aspect. – Erik Funkenbusch Feb 28 '12 at 22:01
  • 1
    Correct me if I'm wrong, but won't this overwrite the master Web.config file? Then any future transformations will be based on the newly transformed Web.config? Could get in a big mess? – Chris Haines May 30 '12 at 12:18
  • This seems like a solution: http://stackoverflow.com/questions/2932059/managing-web-config-for-teams-in-vs2010-tfs – Chris Haines May 30 '12 at 12:59
2

I would suggest using the configSource attribute in the web.config entries for debug builds. Then, in your test and releae builds you can use data transformations to insert the testing and production entries.

You would do something like this:

<connectionStrings configSource="myLocalConnectionStrings.cfg" />

Then you have a local file called myLocalConnectionStrings that you don't check into source control. In your Web.config.Release you simply transform the connectionStrings section to include the production strings and remove the configSource attribute.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • I like this for connect strings but when you deal with other settings (app settings, log configurations, etc) without a custom config provider, is there any other choice besides transformations or ...? – Adam Tuliper Feb 28 '12 at 22:57
  • @AdamTuliper - it works for anything in the web.config. I think it only applies to sections though, not individual entries. So you have to include all entries in your local version. FYI, you can include the inner stuff in the section, but it will be ignored. This gives you a nice template to create your custom file. – Erik Funkenbusch Feb 28 '12 at 23:18
1

As Adam said in his answer, you can kind of do this using web.config transforms. Basically you'd have to create a new solution configuration for each environment. Note that having one for each developer will likely quickly become unmaintaniable, as each configuration / platform combination can have it's own build settings.

Also, the transforms are ONLY applied during the web site packaging (calling the Package target). So if you're trying to use this so that joe and sally can have different configs on their own machine, this won't do that for you.

In that case you're probably better off trying to get everyone on the same configuration, than allowing configs to fragment. The more differences between each environment, the harder time you'll have deploying.

Andy
  • 8,432
  • 6
  • 38
  • 76
  • Agree, having individualized configuration could create a maintenance mess, but our team is small and i hope they know what they doing as well :) – thanikkal May 12 '11 at 05:09
  • 1
    Ok, also keep in mind that you're devs will have to Package the project for the transform, and then install it. So the transforms won't help day to day development. – Andy May 12 '11 at 12:26
0

Here is a T4 solution. This worked for my case because this was an internal tool that would only be used by developers and because I don't need further processing for the "included" files.

File name App.tt.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".config" #>
<#
string pathToConfigurations = Host.ResolvePath("Configurations");
string pathToMachine = Path.Combine(pathToConfigurations, Environment.MachineName + ".config");
if (File.Exists(pathToMachine))
{
    Write(File.ReadAllText(pathToMachine)); 
}
else
{
    Write(File.ReadAllText(Path.Combine(pathToConfigurations, "App.config")));  
}
#>
Karson
  • 449
  • 6
  • 11