Skip Navigation Links

Working with multiple conflicting config files

Categories

One of the luxuries of working in a corporate environment is the uniformity (and I never thought I'd say that). You're in control of just about everything - you can ensure that all developers are using the same configuration. You can all use the same databases, the same web services, the same web sites.

Working on a distributed, open source project is entirely different. You're not in control of the environments being used. Different developers will have things set up differently. And all this is reflected in different web.config files.

There's just one teensy-weensy problem. The web.config file is under source control, which means there is only One True Copy. And if I change it to get my site working, I change it for everyone else, and break theirs.

Of course I'm talking about SingleUserBlog, and of course, this is a problem we've hit. We're all using a different config to each other, and I've already checked in a config file that won't work for everyone else.

So I've had a play around and I've come up with a solution. It's teetering dangerously close to over-engineered, but it also does the job nicely, and I learned a few things to the bargain.

Since SingleUserBlog uses Web Application Projects, it's an msbuild-based project. And the nice thing about msbuild-based projects is that you can edit the project file, add your own tasks in and Visual Studio will still quite happily run with it.

The idea is quite straightforward. Get rid of the web.config file. Create a default.config file that contains the default settings for the project - the one you'd be happy to release. Then, change the .csproj file to copy this file to web.config and use the ReplaceConfigSections task from the Web Deployment Projects add-in to replace any sections in the file with xml from external files.

The implementation is perhaps a little awkward, but it's not exactly horrible either. First, in the main .csproj file, import a MergeConfigFiles.targets file that does the heavy lifting. This new file needs to set up a UsingTask to include the ReplaceConfigSections task. It also needs to set up a few default properties, such as the name of the config files to copy ("default.config" to "web.config"). It then imports another .targets file, if it exists. This one is $(USERNAME)-MergeConfigFiles.targets. If it doesn't exist, the default.config file just gets copied, unchanged to web.config. If it does exist, it should be another msbuild project that simply sets up an ItemGroup containing ConfigReplacementFiles items. Each item points to a file that just contains xml that will replace a specific section in the web.config file. That section name is defined in the Section metadata item in the ConfigReplacementFiles item. The ItemGroup is fed into the ReplaceConfigSections task.

When a build is run, Visual Studio will copy across the default config file, then process this copy, replacing all the sections listed with the contents of all the files listed. If any of the files change, or if the default file changes, it gets rebuilt. It even deletes the web.config file on clean.

There are a couple of issues with this method, but I don't think any are showstoppers. It's perhaps not ideal to have to define this with an msbuild project file, but it's not difficult. Here's an example:

<Projectxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ConfigReplacementFilesInclude="compilation.config">
      <Section>system.web/compilation</Section>
    </ConfigReplacementFiles>
    <ConfigReplacementFilesInclude="authentication.config">
      <Section>system.web/authentication</Section>
    </ConfigReplacementFiles>
  </ItemGroup>
</Project>

Not exactly difficult. Another drawback is that if you change this .targets file, the changes are not reflected until the project is reloaded. Not ideal, but not major, either. Oh, and it will only work for web.config, too - the web deployment project task assumes that. Supporting app.config wouldn't be too tricky either, but would require a custom task.

One drawback that could actually cause problems is ensuring that your overrides don't override something new, or you only add new things to your overrides, but this is really just something you need to keep track of.

Another interesting drawback is that asp.net will automatically recreate the web.config file for you if it gets deleted. Somehow it knows to call the .csproj file and get it created correctly - well, mostly correctly. It did generate some things incorrectly while testing that, and it always did a full build after that anyway, so everything still worked correctly.

The only files that get into source control are default.config and the initial MergeConfigFiles.targets. The username based file doesn't get added, and neither do the local changes.

It might be easier to download an example and actually try it out. Let me know what you think.

posted 9/12/2006 12:26:35 AM

 

Comments:

# Nice one!
posted by Darren Neimke on 9/13/2006 1:55:33 AM :

Hi Matt, this seems like a good path - well done for doing the research on it. So basically, we just create a standard Web.config file which might use (say) the FileSystemProvider for SUB and the AccessProvider for ASP.NET API's and then allow each developer to create their own specific replacement file overrides.

# What about configSource?
posted by Fabrice on 9/14/2006 2:32:13 PM :

Did you think about using the configSource attribute to externalize the configuration sections that could be customized? This way you'd just have to replace the external configuration files with the custom ones.

For example, in web.config you'd have:
<system.web>
<compilation configSource="compilation.config" />
<authentication configSource="authentication.config" />
</system.web>

and the actual configuration in the compilation.config and authentication.config files.

# configSource
posted by Matt Ellis on 9/14/2006 4:28:44 PM :

Yeah, it's an option. But you can't provide a default set of values if you use configSource, so you'd need the target file to be always there. This means these files need to be checked into source control too, and then you're back where you started.

# Multiple Web.config files
posted by Donnie Hale on 9/14/2006 7:40:23 PM :

I'm not sure why this is such a difficult issue. I have multiple Web.config files (Web.dev.config, Web.qa.config, Web.prod.config, etc.). Within VS, I use Web.dev.config (renamed, obviously, to Web.config).

When it's time to push to an environment, the build/stage scripts pick the right .config file for the targeted environment, run "aspnet_compiler", and deploy the results.

That's worked fine for me for numerous apps. What am I missing?

Donnie

# Multiple web.configs
posted by Walt Ritscher on 9/14/2006 10:00:43 PM :

We use MSBuild for our web application project too. I agree with Donnie Hale though. We have multiple configs (web.config.dev, web.config.testserver, web.config.prodserver, web.config.default). MSBuild determines which one to deploy and copies it to the build folder. Each config is under source control. Works for us.

# Multiple editors of web.config
posted by matt on 9/14/2006 10:12:51 PM :

Your approach works great for different environments (I use at work). The problem here is different in that we've got multiple people working on the one web.config at a time. E.g. I want to use a Microsoft Access based profile provider, and another guy wants to use a Sql Server one. If we have just one web.config file, it's going to be in source control. If I change it to use Access, I'll check that in and cause problems for the other guy using Sql Server.

I could check in multiple web.config files (web.matt.config, web.darren.config, etc) but then we don't have a default config file set (and this doesn't really scale for more developers).

Instead, I have one default.config file in source control, this is the default web.config file that will get used, and any changes will end up back in source control. If a developer wants to have a different config than the default, they just override the bits they want changed.

I think there are lots of ways around this problem - I was looking for a low friction, automated solution.

And I think perhaps I titled this post wrong - it's not "working with multiple conflicting config files" but "multiple people making conflicting changes on one config file"

# Multiple web.configs
posted by Walt Ritscher on 9/14/2006 10:22:20 PM :

We use MSBuild for our web application project too. I agree with Donnie Hale though. We have multiple configs (web.config.dev, web.config.testserver, web.config.prodserver, web.config.default). MSBuild determines which one to deploy and copies it to the build folder. Each config is under source control. Works for us.

# Are comments broken?
posted by Matt Ellis on 9/22/2006 10:34:56 PM :

Testing comments. Might have broken something...

# RE: Working with multiple conflicting config files
posted by Dave Moyle on 4/4/2007 7:44:03 AM :

Your sample link is broken.

The type or namespace name 'RewritingStaticFileHandler' could not be found (are you missing a using directive or an assembly reference?)

# RE: Working with multiple conflicting config files
posted by Matt Ellis on 4/4/2007 10:48:59 PM :

Ooops. Fixed the link - should be working now.

Thanks for letting me know!

# RE: Working with multiple conflicting config files
posted by Rajesh on 6/19/2007 11:31:07 AM :

Good Article, but please make the font-color as black. It was difficult to read the article.

Thankyou

# RE: Working with multiple conflicting config files
posted by Matt Ellis on 6/19/2007 3:40:19 PM :

Good point. Done! Now in lovely black on white.

 

Name:  
Url (optional):  
Subject:  
Comment:  

Enter Captcha Validation:
(If you cannot read the Captcha image, press "Reset Image" to generate a new one)