98

We have several .NET projects where we store certain settings in configuration files.

Now each developer will have their own configuration files that differ a little (different connection strings to connect to local databases, different WCF endpoints, etc.)

At the moment we tend to check out app/web.config files and modify them to suit our needs.

This leads to many problems since from time to time someone will check in their own settings or loose custom configuration when getting latest version from TFS.

How do you deal with situations like this? Or don't you have this problem at all?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
twarz01
  • 981
  • 1
  • 7
  • 3
  • 10
    I am voting to reopen, since this is a common problem for visual studio developers and directly involves the tools, developers are using. (hence the votes) – Christian Gollhardt Jun 09 '17 at 17:25
  • Agreed, this is a persistent issue as our team size has grown and various attempts at solving have been unsatisfactory. – DiskJunky Oct 16 '17 at 09:24

9 Answers9

67

We use a system that combines several of the existing answers on this page, plus draws on this suggestion by Scott Hanselman.

In short, what we did was to have a common app.config / web.config, and to have most of the specific settings in individual files, as suggested by other answers here. e.g. for our SMTP settings, the app.config contains

<system.net>
  <mailSettings>
    <smtp configSource="config\smtp.config" />
  </mailSettings>
</system.net>

This file is in source control. However, the individual files, like this, are not:

<?xml version="1.0" encoding="utf-8" ?>
<smtp deliveryMethod="Network">
  <network host="127.0.0.1" port="25" defaultCredentials="false" password="" userName ="" />
</smtp>

That's not quite where the story ends though. What about new developers, or a fresh source installation? The bulk of the configuration is no longer in source control, and it's a pain to manually build all of the .config files they need. I prefer to have source that will at least compile right out of the box.

Therefore we do keep a version of the .config files in source control, named .config.default files. A fresh source tree therefore looks like this:

alt text

Still, not really any use to the developer, since to Visual Studio they're just meaningless text files. Hence the batch file, copy_default_config.bat, takes care of creating an initial set of .config files from the .config.default files:

@echo off
@REM Makes copies of all .default files without the .default extension, only if it doesn't already exist. Does the same recursively through all child folders.
for /r %%f in (*.default) do (
    if not exist "%%~pnf" (echo Copying %%~pnf.default to %%~pnf & copy "%%f" "%%~pnf" /y)
)
echo Done.

The script is safely re-runnable, in that developers who already have their .config files will not have them overwritten. Therefore, one could conceivably run this batch file as a pre-build event. The values in the .default files may not be exactly correct for a new install, but they're a reasonable starting point.

Ultimately what each developer ends up with is a folder of config files that looks something like this:

alt text

It may seem a little convoluted, but it's definitely preferable to the hassle of developers stepping on each other's toes.

Gavin
  • 9,855
  • 7
  • 49
  • 61
  • Why not having directly the main .config in the repository but not the individual ones? – graffic Nov 21 '11 at 08:17
  • 6
    What a pain, we need a better solution for this. I had a similar method set up and got tired of it being so fragile and still needing explanation to new developers. – jpierson Jan 20 '12 at 07:49
  • @Gavin, I get an error if I try to run the bat file you have outlined: '∩╗┐@echo' is not recognized as an internal or external command, operable program or batch file. – Austin Jun 19 '15 at 13:48
  • 2
    @Austin, you seem to have some non-printable garbage before the '@echo', part of which is possible the Unicode byte order mark? Maybe something went wrong in the copy/paste, or the batch file is saved in an encoding that can't be properly read. – Gavin Jul 13 '15 at 01:41
22

In your Web.config use source from other files

<configuration>
    <connectionStrings configSource="ConnectionStrings.config" />
...
</configuration>

Keep the web.config in version control and don't do it for ConnectionStrings.config. Now all developers have one file for the connection string.

You can do this for all the settings that are local dependant.

Filipe Pinheiro
  • 1,082
  • 8
  • 32
  • 1
    This worked nicely for me. See also: http://davidgiard.com/2012/05/25/MovingConfigSectionsToExternalFiles.aspx The ConnectionStrings.config file has to be in the same folder as the app or web config. Also, you must change its properties to 'copy if newer' to output. And the ConnectionStrings.config file needs the full section with opening and closing elements. You also have to set version control to ignore the file so each is user-specific. – Jeffrey Roughgarden Apr 17 '17 at 18:24
  • 1
    I used the option since I had to merge settings – Spikolynn Aug 23 '17 at 06:05
  • 1
    @JeffreyRoughgarden If you include the ConnectionStrings.config file in Solution Explorer, does the file not have to exist on the filesysterm? And if it does, that means you've checked it into source control. Which is the initial problem we were trying to get away from. – Jez May 28 '18 at 18:32
20

Here a solution for web.config files and Visual Studio 2010:

1) Edit manually your web application .csproj file to add an AfterBuild Target like this:

  <Project>
   ...
    <Target Name="AfterBuild">
      <Copy SourceFiles="web.config" DestinationFiles="obj\$(Configuration)\tempweb.config" />
      <TransformXml Source="obj\$(Configuration)\tempweb.config"
                  Transform="web.$(USERNAME).config"
                  Destination="obj\$(Configuration)\tempweb2.config" />
      <ReadLinesFromFile File="obj\$(Configuration)\tempweb2.config"><Output TaskParameter="Lines" ItemName="TransformedWebConfig"/></ReadLinesFromFile>
      <ReadLinesFromFile File="web.config"><Output TaskParameter="Lines" ItemName="UnTransformedWebConfig"/></ReadLinesFromFile>
      <Copy Condition=" @(UnTransformedWebConfig) != @(TransformedWebConfig) " SourceFiles="obj\$(Configuration)\tempweb2.config" DestinationFiles="web.config" OverwriteReadOnlyFiles="True" />
    </Target>
  </Project>

This target will transform the Web.config file corresponding to the current developer logged in - hence the $(USERNAME) variable -, with the corresponding file created in 1). It will replace the local Web.config only if the content has changed (to avoid restart) at each build, even if the local Web.config is source controlled, that's why there is the OverwriteReadOnlyFiles is set to True. This point in fact is arguable.

2) Create a file named Web.[developer windows login].config for each developer in the project. (for example in the following screenshots I have two developers named smo and smo2):

enter image description here

These files (1 per developer) can/should be source-controlled. They should not be marked as dependent on the main Web.config because we want to be able to check them out individually.

Each of this file represent a transformation to apply to the main Web.Config file. The transformation syntax is described here: Web.config Transformation Syntax for Web Application Project Deployment. We reuse this cool Xml file transformation task that comes out-of-the-box with Visual Studio. The purpose of this task is merge Xml elements and attributes instead of overwriting the whole file.

For example, here is a sample web.[dev login].config that changes a connection string named 'MyDB', regardless of the rest of the Web.config file:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
      <add name="MyDB" 
        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
    </connectionStrings>
</configuration>

Now, this solution is not perfect because:

  • after building, developers may have a different Web.Config locally than in the source control system
  • they may have to force local write when getting a new Web.Config from the source control system
  • developers should not check out/in the main web.config. It should be reserved to few people.

But at least, you only have to maintain a unique main web.config plus one transformation file per developer.

A similar approach could be taken for App.config (not web) files but I did not elaborate further.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
2

We are using machine.config to avoid having differences in web.config between the environments.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
0

How about ignoring the file, so it never gets checked in? I've run into a similar issue and have added web.config to the ignore list in Subversion.

In TFS though, it's a little bit harder, see this post on how to do it.

Community
  • 1
  • 1
Marko
  • 71,361
  • 28
  • 124
  • 158
0

One way you could cope is to have a tokenised system and use a rake script to change the values.

A more rudimentary method could be to have a link to an AppSettings.config file for all AppSettings in your web.config (similar with connections) i.e.

<appSettings configSource="_configs/AppSettings.config" />

Then have a folder with each of your developers have a version in a sub folder (i.e. /_configs/dave/). Then when a developer is working on their own code they copy from the sub folder to the root of the linked folder.

You will need to make sure you communicate changes to these files (unless you tokenise). If you keep the AppSettings.config file out of source control and only check in the devs individual folders (all of them) then they will be forced to copy the correct one.

I prefer tokenisation but can be tougher to get up and running if this is only meant to be a quick fix.

ArtificialGold
  • 825
  • 5
  • 14
0

Ignore the files and have a Commom_Web.Config and Common_App.Config. Using a continuous integration build server with build tasks which rename these two to normal names so that the build server can do its job.

Arthis
  • 2,283
  • 21
  • 32
  • this needs to be done on dev machines, before it goes to build server – twarz01 Sep 14 '10 at 10:10
  • No this renaming should happen on the build server. Common config files being stored on the subversion, they are independent of a specific developper. Whereas every developper runs its personnal config files to develop on his machine, and do not care of submitting it because it is not part of the subversion. – Arthis Sep 14 '10 at 10:21
  • The thing this doesn’t support is developers debugging the application. The root `web.config` in the source folder is used during debugging. If you, e.g., added `web.config` to `.gitignore` and either required users to copy `Commom_Web.Config` to `web.config` and edit it to their fancy, you’re part of the way there. But then when you need to edit something which is the same on all developer’s machines, such as the `system.web/compilation` section or particular `appSettings` which should be the same everywhere, this scheme falls apart. – binki Sep 20 '18 at 15:45
0

We have the same problem and what we are doing is

  • Check in the web.config with testserver / production values
  • Developer goes to windows explorer and change the read only mode of the file
  • Edit the config to suit to their environment.
  • Check in into the web.config happens only when deployment values changes or any config entry is added or deleted.
  • This needs a good amount of comment for each config entry as it needs to be changed by each dev
Joy George Kunjikkuru
  • 1,495
  • 13
  • 27
  • 1
    This may work better in shops where the source control you use sets fields as Read Only by default but for others using Subversion it might not work as well. We could set read only permissions in the repository to preventing it from being committed however that would need to be set on each config file for each branch. – jpierson Jan 20 '12 at 21:35
  • For those of us not using something like TFS, it isn’t obvious how this solution could work. Could you give a little more background? Also, doesn’t it require developers to be careful and not accidentally checkout the file? – binki Sep 20 '18 at 15:48
-1

Assuming you are using Visual Studio, why don't you use different Solution Configurations? For example: you can have a Debug config which uses Web.config.debug and a Release config which uses Web.config.release. Web.config.debug should have something like

<appSettings file="C:/Standard_Path_To_Configs/Standard_Name_For_Config.config"> 

with the file Standard_Name_For_Config.config that gots all the personal developer settings, while the Web.config.release always has the production settings. You can store default configs in some source controlled folder and make a new user take them from there.

  • Then each individual user who would ever contribute to the project would need to have their own build configuration. This does not seem to scale and does not support projects which accept contributions. – binki Sep 20 '18 at 15:47