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
I've seen the connectionString.config file excluded from source control completely. So everybody machine/developer has to have their own.
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 :-)
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: <connectionStrings configSource="dev.connectionStrings.config" />
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 (<appSettings file="dev.app.config">)
Hmm, it didnt like my xml snippet, here's a gist instead: https://gist.github.com/1366740
@Daniel You can use web.config transformations on app.config files, see http://philbolduc.blogspot.com/2010/03/using-config-transforms-outside-web.html
I wrote NConfig (https://github.com/Yegoroff/NConfig) project that exactly solves this issue.
You can also use web.config transformations where each profile is a different user/machine.
@Morgan, cool, didn't know that!
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!
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.
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.
+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.
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}
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.
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
I use the method described by Jonathan Sewell.
In SVN I just ignore to connection string config file so it's never checked in.
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!
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?
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
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.
How would this work if you are on a cloud based (web farm) solution? Would this still work?
@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.
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
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)
Have you considered using a "user.config" file?
<appSettings file="user.config">
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
Use a smudge/clean script in git to massage the connection string. That's an option as well.
Adam
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.
"... 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
Simple and neat work Ayende :)
Comment preview