Ayende @ Rahien

Refunds available at head office

Frictionless development: Web.config and connection strings

This is something that I actually run into a lot at customer sites. They have a lot of friction during development between different connection strings that developers use during development. For example, we may have one developer using:

<add name="RavenDB" connectionString="Url=http://localhost:8080" />

While another is using:

<add name="RavenDB" connectionString="Url=http://localhost:8191;Database=TheApp" />

This usually causes a hell of a lot of trouble in most teams (or maybe you have developers that use SQL Express, and some who installed SQL Development, etc).

That is friction, and you want to deal with that as soon as possible. The easiest thing to do is actually:

<add name="Ayende-PC" connectionString="Url=http://localhost:8080" />
<add name="Ayende-Laptop" connectionString="Url=http://localhost:8191;Database=TheApp" />

This works, because the connection string name is now the machine name (System.Environment.MachineName). That is a great first step, because it means that you can get things done without fighting over the connection string in the web.config.

Another alternative is to have a default connection string, and allow to “override” it with the specification of a connection string specific for the machine.

It is a small thing, but it actually helps quite a lot. You can extend this to other settings as well. For apps that have a lot of settings, I usually take them out of the Web.config into a Default.config file, and the configuration reader is set to look for [MachineName].config first, and only then at the Default.config file.

Comments

Jonathan Sewell
11/15/2011 10:13 AM by
Jonathan Sewell

I've seen the connectionString.config file excluded from source control completely. So everybody machine/developer has to have their own.

Daniel Lidström
11/15/2011 10:33 AM by
Daniel Lidström

Put connection strings in your machine.config. This solves all differences between developer machines. Only problem left to solve is if there is an unwanted override in your App.config or Web.config, that you don't want to remove. In this case, for Web.config simply create a Web.Release/Debug.config transformation. For App.config this won't work out of the box (Visual Studio does not support transforming App.config), but the SlowCheetah plugin solves that part for you :-)

Morgan Persson
11/15/2011 10:36 AM by
Morgan Persson

You can also use the configSource attribute on the connectionStrings node to enable this. You just have to agree on the filename of the configSource in development. ex:
Then you can use the same to handle all other environments, like build, For test you change configsource to test.connectionstrings.config etc.

You can use the same technique on the appSettings node to with the file attribute ()

Morgan Persson
11/15/2011 10:43 AM by
Morgan Persson

Hmm, it didnt like my xml snippet, here's a gist instead: https://gist.github.com/1366740

Morgan Persson
11/15/2011 10:46 AM by
Morgan Persson

@Daniel You can use web.config transformations on app.config files, see http://philbolduc.blogspot.com/2010/03/using-config-transforms-outside-web.html

Alexander Yegorov
11/15/2011 11:16 AM by
Alexander Yegorov

I wrote NConfig (https://github.com/Yegoroff/NConfig) project that exactly solves this issue.

Henning Anderssen
11/15/2011 11:21 AM by
Henning Anderssen

You can also use web.config transformations where each profile is a different user/machine.

Frank Quednau
11/15/2011 12:01 PM by
Frank Quednau

@Morgan, cool, didn't know that!

JV
11/15/2011 02:59 PM by
JV

My co-worker, Tyler, created a template generation process for managing environment configurations. We used to use Brail, but now we use the Razor template engine. It's pretty simple (we use NAnt, but this could easily be ported to MSBuild).

Folder layout: .\env\dev1.properties .\env\dev2.properties .\env\qa.properties .\env\production.properties .\env\templates\app\App.config.cshtml .\env\templates\web\Web.config.cshtml

nant config build -D:env=dev1 nant config build -D:env=dev2 nant config build -D:env=qa nant config build -D:env=production

Config files are generated to: .\env\generated\app\App.config .\env\generated\web\Web.config

Finally, the generated config files are referenced in the projects.

It works great!

Allan Nienhuis
11/15/2011 04:24 PM by
Allan Nienhuis

I've used the approach Ayende describes in many projects, and it works great - it's simple to understand and use, and trivial to extend. Nothing magical happens at any time. Sometimes I've added the site path to the key (machineName/siteName) which allows devs to keep multiple configurations for the same app without constantly modifying the config file.

ConfigurationManager
11/15/2011 06:09 PM by
ConfigurationManager

AFAIK, the web.config transformation works only for deployment. in multi developers environment it will be useless as you can't have your own configuration using the custom configuration transformation.

I know there are (dirty) ways to solve this issue but it's not out of the box configuration.

TLK
11/15/2011 06:36 PM by
TLK

+1 to Alexander Yegorov. NConfig pretty much covers this and other configuration issues. Each developer in our team can setup his custom configuration with ability to override only the property he want to override. And this does not need a separate interface to work with configuration. All code (except of one line on application start) remains the same.

Matt McElheny
11/15/2011 06:44 PM by
Matt McElheny

I've always used a variation on this that first defines an environment based on machine name, and then the settings are defined based on environment. This also allows me to put all the settings in one file so there's less difference in settings from local to dev to test to prod. Of course, I store almost every configurable variable except for connection strings in the database anyway. My configs go something like this:

{appSettings} {add key="environment:MyLaptopName" value="WORKSTATION:" /} {add key="environment:MyDesktopName" value="WORKSTATION:" /} {add key="environment:DevServerName" value="DEV:" /} {add key="environment:ProdServerName" value="PROD:" /} {/appSettings}

{connectionStrings} {add name="PROD:MyDb" connectionString="..." providerName="System.Data.SqlClient" /} {add name="DEV:MyDb" connectionString="..." providerName="System.Data.SqlClient" /} {add name="MyDb" connectionString="..." providerName="System.Data.SqlClient" /} {/connectionStrings} {!-- no env prefix means fall thru --} {/connectionStrings}

flukus
11/15/2011 08:07 PM by
flukus

I always preferred to just not have the web.config under source control. Generally I like having a config template and a build Target to generate the real config.

Restuta
11/15/2011 09:19 PM by
Restuta

I was also using NConfig, it works better that post builds events and do not requires custom code and moreover it can be used for any configuration, not only for connectionStrings

NC
11/15/2011 11:06 PM by
NC

I use the method described by Jonathan Sewell.

In SVN I just ignore to connection string config file so it's never checked in.

Sybil Wieners
11/16/2011 05:15 AM by
Sybil Wieners

Great thoughts guys. I used the same variation as described by Matt. This post is really helpful as much as the participants of the discussion. Keep it up!

Chuck Durfee
11/16/2011 05:16 AM by
Chuck Durfee

Interesting idea. If you are using MachineName as an identifier, how would you approach web farms and other instances where multiple machines need the same configuration file?

Avi
11/16/2011 06:07 AM by
Avi

I, as Alexander Yegorov, have also built an OS library that solves this issue, it is also called NConfig (I guess I'll have to change the name :-))

this library takes a different approach by abstracting away the configuration source and not necessarily using System.Configuration, different sources can be used and merged together.

it also allows defining multiple keys for a configuration value and enable the configuration being managed as one set that gets deployed with the application to all environments. example:

env:dev\machineName:machine1 -> conn str 1 env:dev\machineName:machine2 -> conn str 2 env:staging\machineName:all nachines -> conn str 3

I've posted an introductory post about it here: http://designingsoftware.net/2011/08/15/net-configuration-made-easy-with-nconfig/ link to the repo: https://github.com/Avi-Levi/NConfig

Gian Maria
11/16/2011 08:30 AM by
Gian Maria

I like very much ayende solution, I really do not like to add extra complexity of adding a configuration library over the configuration library already included in .Net.

Using a multiple config file, (http://blog.andreloker.de/post/2008/06/16/Keep-your-config-clean-with-external-config-files.aspx) is usually the best solution.

Richard Wilde
11/16/2011 09:20 AM by
Richard Wilde

How would this work if you are on a cloud based (web farm) solution? Would this still work?

Avi
11/16/2011 09:24 AM by
Avi

@Gian Maria I think Ayende's solution is great and would recommend taking this approach for the simpler scenarios. but when you have lots of configuration values that differentiates according to multiple environmental properties (environment,machine name etc...) and you use this solution, you actually implement this kind of library yourself.

Leblanc Meneses
11/17/2011 08:07 AM by
Leblanc Meneses

for more complex scenarios I had to build FlexibleConfigTask as our solution to config change management.

you can see the perspectives on why it was built on the blog

http://www.robusthaven.com/blog/continuous-integration/MSBuild-FlexibleConfigTask

internally it uses parsing expression grammer and I then send to ICodeCompiler

the largest team currently using this is a team of ~25+ devs with unique values, various hyper-v integration branches, and production - it works

andrew.wait
11/18/2011 09:39 AM by
andrew.wait

What we do Map a virtual directory /ini to a location outside the site (do this on all developer/test/live sites; this is actually done as part of our build script now)

Put in .ini a file with all your settings in this location

On application start server.mappath and read the .ini file and put the settings into an application object (or whatever you desire)

Now developers can have whatever settings they like and you never need to update any web.config file...

Need new settings... just add them to the ini file (this then becomes a simple release issue)

Ras Fred
11/19/2011 04:10 AM by
Ras Fred

Have you considered using a "user.config" file?

santosh kumar patro
11/19/2011 08:26 AM by
santosh kumar patro

Hi,

I am using VS2010 and it has multiple web.config based on the deployment enviroment (i.e web.Dev.config,web.Int.Config.web.Prod.config.web.Debug.config). In development environment I am using the main web.config. In the main web.config I have some settings for ADFS authentication and it depends on the machine name of the developer. The settings varies in individual developer machines. Let say I am working on the web.config for some changes and then I checkin the web.config changes then it is going to impact the other developer web.config changes if they are taking the latest code from the TFS source safe.

I tried to override the web.config file settings using the Application_Start event of the Global.asax file but it is causing a restart to the IIS which is not desirable in my case.

Hence can you provide me any solution regarding how to manage the settings in the web.config file specific to the machine name.

Any help on this will be appreciated.

Thanks & Regards, Santosh Kumar Patro

Adam Dymitruk
11/21/2011 10:36 AM by
Adam Dymitruk

Use a smudge/clean script in git to massage the connection string. That's an option as well.

Adam

Chris Smith
11/24/2011 09:10 AM by
Chris Smith

I think the J2EE guys are way ahead of the .Net guys in this particular problem. On all but fairly small products, the runtime configuration of UAT/production environments is out of the hands of the development team and is not stored in source control for security reasons (doing this is a sin!). J2EE app servers inherit the role of configuration management and provide named resource to configuration variables such as connection strings to the applications hosted inside them. .Net provides this with appSettings and connectionStrings elements.

On this basis, which I find appealing and appeases all parties involved, I'd rather people used the machine.config and used well known named connection strings and app settings as it decouples configuration from the software environment and source control.

Also, to deploy your software you can just dump the artifacts on the server then without any magic config transformation junk.

Bo Hansen
11/25/2011 02:33 PM by
Bo Hansen

"... and the configuration reader is set to look for [MachineName].config first, and only then at the Default.config file"

At the risk of exposing my own ignorance ... How do you do this?

Regards Bo

Arul
12/27/2011 10:19 AM by
Arul

Simple and neat work Ayende :)

Comments have been closed on this topic.