In the previous post, I dealt with the standard .NET config file. It would be lovely if all we needed to deal with was appsettings and connection strings. Sadly, the world is more complex than that. Now, quite a few of the systems listed below (pretty much all of the XML-based ones) give you the option of including their config in the app.config or in a separate file. I can’t say as I see it makes a blind bit of difference. Each of them has their own XML schema and I doubt you wish to write a diff tool for each of them.
Castle Windsor has a fluent configuration mechanism and an XML format. Annoyingly, neither of them support environmental diffs. I can’t quite believe that this isn’t baked in, but then the major competitors don’t seem to address this either. Furthermore, since there is no facility within Windsor for producing a container that specializes another container, you can’t use specializations to produce environmental deltas. In particular, you could have the principal configuration using the fluent interface, keeping with ThoughtWorks dictum of not putting anything into a config that can be hardwired. You could then specialize using the XML format. There is, however, one other alternative, Binsor. Now, Binsor is a true .NET language with a couple of specializations to support windsor configuration (or a DSL, if you prefer). It supports environmental deltas through the Extend macro. The Extend Macro isn’t really documented, so here’s a quick guide to how to use it:
Component "service", IService, ConcreteService:
standardParameter = "Value the same across all deployments"
import file from "MainConfiguration.boo"
environmentalParameter = "Parameter value is different in Environment2.boo"
This is incredibly powerful. In particular it lets you change your mind about which parts of the config are environmentally driven and which aren’t. Let’s go through it (with some stuff I learnt the hard way)
- Binsor allows you to include one file in another. You need to wrap the code in the main configuration in a function that you call from the environmental diff. This might seem backwards, but it’s pretty much the only way it can work.
- The import file syntax supports relative paths, but make sure that you pass BooReader an absolute path to the environmental diff, or it may not work the way you’re hoping.
- Extend is keyed by the name of the component that is declared in the MainConfiguration.boo file. Thus, in the example, the ConcreteService class has string parameters for standardParameter and environmentalParameter.
- There is, of course, the outstanding question of how you identify which environmental delta to use. You’ve basically got two options here: get the install to rename the delta file to a standard name, or put the name into the appsettings. (I’m finding it remarkably hard to kill off appsettings completely, much as I try…)
I like Binsor in that it’s a complete solution to the problem. It is, however, quite a heavy-weight solution, and you can’t mix and match it with the fluent configuration since it’s doing all the hard work itself. Since Castle Windsor’s ComponentModel class is immutable and doesn’t support specialization, it has to build its own component model in order to support this feature. That shouldn’t bother you until you try stepping through the code and discover that there’s several thousand more lines supporting this syntax than you were expecting. A more general difficulty is that it uses Boo’s most powerful and most dangerous feature: the ability to change the way the syntax tree is evaluated. It produces a relatively elegant syntax, but it’s not documented and there’s no editor support for it that I know of.