views:

381

answers:

4

I've been struggling with this problem, and while something elegant would be preferred any old hack will do at this point :)

I have a repository that is registered something like:

<castle>
<properties>
        <myConnStr>Data Source=COMPUTERNAME\SQL2008;Initial Catalog=MyDB;Persist Security Info=True;User ID=username;Password=password</myConnStr>
</properties>
<components>
        <component id="MyRepository" service="DomainModel.Abstract.IMyRepository, DomainModel" type="DomainModel.Concrete.MyRepository, DomainModel" lifestyle="PerWebRequest">
            <parameters>
                <connectionString>#{myConnStr}</connectionString>
            </parameters>
        </component>
</components>

I am trying to set up my config file so that I don't have to modify the myConnStr property portion for the various deployment options. It's fine (even ideal) if I can only establish the connectionString once when the configuration is first loaded, but I can't figure out how to set this value when setting up my container.

If I wasn't using castle windsor I'd just have multiple connection strings defined like "connectionStringStaging", "connectionStringProduction" and choose the appropriate one via code based on my environment (computer name or URL). How can I do something like that with a castle windsor property? Modifying the 'parameter' for the component instead of the 'property' would be acceptable as well.

A: 

Can't you do something like ...

<castle>
<properties>
        <dev>Data Source=COMPUTERNAME\SQL2008;Initial Catalog=MyDB;Persist Security Info=True;User ID=username;Password=password</dev>
        <stage>Data Source=COMPUTERNAME\SQL2008;Initial Catalog=MyDB;Persist Security Info=True;User ID=username;Password=password</stage>
        <production>Data Source=COMPUTERNAME\SQL2008;Initial Catalog=MyDB;Persist Security Info=True;User ID=username;Password=password</production>
</properties>
<components>
        <component id="MyRepository" service="DomainModel.Abstract.IMyRepository, DomainModel" type="DomainModel.Concrete.MyRepository, DomainModel" lifestyle="PerWebRequest">
            <parameters>
                <connectionString>#{dev|stage|production}</connectionString>
            </parameters>
        </component>
</components>

Otherwise have a look at using the configSource attribute and storing the environment settings in 3 separate config files. http://weblogs.asp.net/fmarguerie/archive/2007/04/26/using-configsource-to-split-configuration-files.aspx

The configuration choice should be an explicit activity external to the codebase.

Neal
"#{dev|stage|production}" -> AFAIK this syntax is not supported by Windsor...
Mauricio Scheffer
"The configuration choice should be an explicit activity external to the codebase." -> I agree, but if the OP wants to select the connection based on computer name or URL, there has to be code somewhere...
Mauricio Scheffer
I agree also, but I haven't liked any of the solutions which supports this approach. Modifying the web.config before every publish is very error-prone and the various solutions for automatic web.config merging/updating (msbuild customizations, web deployment projects, etc) are irritatingly complex. This is OK for large projects but in my environment we support hundreds of applications and have junior developers who need to be able to grab the solution from source safe, make a quick change, and publish. Hopefully the 'transforms' solution in VS2010 will do the trick.
Shaun Rowan
the dev|stage|production was meant to indicate OR as in choose one of these and stick in this spot =)Personally I use the configSource attribute and store template files in source control for the variations, this means you can checkout / clone the source, copy the template and rename it, edit any settings required and you are good to go. If a junior dev can't do that - they should go back to kindergarten =)
Neal
+2  A: 
IWindsorContainer container = ...
container.Register(Component.For<IMyRepository>().ImplementedBy<MyRepository>()
    .LifeStyle.PerWebRequest
    .Parameters(Parameter.ForKey("connectionString").Eq(GetConnectionStringFromEnviroment())));

where GetConnectionStringFromEnviroment() gets the appropriate connection from the <connectionStrings> section of your config or anywhere you want.

If you need this to be in XML config instead of code, you could:

  • write an ISubDependencyResolver for that specific parameter.
  • use defines and ifs (this is probably the easiest solution if you're already using a xml config)
Mauricio Scheffer
I ended up going with one of the other solutions but this is actually exactly what I was looking for originally - thanks!
Shaun Rowan
A: 

disclaimer: This is something I wrote

FWIW, It's possibly overkill for you, but I've written a deployment-system to handle situations like this (different configurations for different servers); it's called dashy. It's currently in development (but the version you can download should work, to a reasonable degree :P), so I don't recommend it for immediate live use; but feel free to give it a crack in a dev/staging environment and see if it's useful. It does more than what you need, but might be of interest :)

Noon Silk
+2  A: 

I'd prefer a simple solution and not push the framework to perform magic for you. I would wrap connection string in another class to provide the right connection to your repositories and add that to the dependency tree.

public interface IConnectionFactory
{
    public string GetConnectionString();
}

Pass this into your 'Repositories' instead of a string.

Maxwell Troy Milton King
Perfect. One of my early workaround ideas was to possibly have my repository take a delegate which returns a string, and to see if I could get Windsor to inject one. I couldn't get it to work but this is basically the same thing.
Shaun Rowan
It certainly works, but if you use this approach for each "dynamic" parameter you end up with a lot of interfaces and classes that could have been easily avoided. Testing becomes harder as well because you need to mock IConnectionFactory instead of just passing a string.
Mauricio Scheffer