Use a registered component in the Castle container to supply a dependency for another component

331 views Asked by At

I have a Castle Windsor container registration class as follows...

public class WindsorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {           
        container.Register(Component.For<IConfigProvider>()
            .ImplementedBy<ConfigProvider>()
            .LifeStyle.Singleton);

        container.Register(Component.For<IDbRepository>()
            .ImplementedBy<DbRepository>()
            .LifeStyle.Singleton                
            .DependsOn(Dependency.OnValue(
                "connectionString", 
                // -- Somehow call GetConfig() on the IConfigProvider --
        )));                
    }
}

The DbRepository needs a connection string to be passed in on object construction. However, that connection string is actually supplied by the IConfigProvider.

Is it possible to get Castle Windsor to somehow resolve the IConfigProvider at this point and call the GetConfig method?

Open to other options if this is not a good idea.

2

There are 2 answers

0
Patrick Quirk On

You could use DynamicParameters to accomplish this:

container.Register(Component.For<IDbRepository>()
    .ImplementedBy<DbRepository>()
    .LifeStyle.Singleton                
    .DynamicParameters((k,d) => 
    {
        var configProvider = k.Resolve<IConfigProvider>();
        d["connectionString"] = configProvider.GetConfig();
    }
)));      

From the documentation:

DynamicParameters works with a delegate which is invoked at the very beginning of component resolution pipeline.

In other words, you'll retrieve the connection string just before the component is resolved.

0
activebiz On

you might wanna checkout castle Windsor's typed factory facility.

https://visualstudiomagazine.com/articles/2011/12/01/windsor-beyond-dependency-injection.aspx

Edit:

Here's an example of exactly what you want to do..

http://joseoncode.com/2011/01/09/windsor-isubdependencyresolver-example/

Implement AbstractFacility which will resolve your connection string

    public class DependenciesFromAppSettings : AbstractFacility
{
    protected override void Init()
    {
        var dic = ConfigurationManager
             .AppSettings
             .AllKeys
             .ToDictionary(k => k, k => ConfigurationManager.AppSettings[k]);

        Kernel.Resolver.AddSubResolver(new DependenciesFromAppSettingsResolver(dic));
    }
}

Implement ISubDependencyResolver

    public class DependenciesFromAppSettingsResolver : ISubDependencyResolver
{
    private readonly IDictionary<string, string> webConfig;

    public DependenciesFromAppSettingsResolver(IDictionary<string, string> webConfig)
    {
        /// you can pass in your configprovider object here (or similar)
        this.webConfig = webConfig;
    }

    public bool CanResolve(Castle.MicroKernel.Context.CreationContext context, 
        ISubDependencyResolver contextHandlerResolver, 
        Castle.Core.ComponentModel model, 
        Castle.Core.DependencyModel dependency)
    {
        //make sure your connectionstring has value
    }

    public object Resolve(Castle.MicroKernel.Context.CreationContext context, 
        ISubDependencyResolver contextHandlerResolver, 
        Castle.Core.ComponentModel model, 
        Castle.Core.DependencyModel dependency)
    {
        //resolve your connectionstring here
    }
}

Your repos looks like as follows:

    public class MyRepo 
{
    public MyRepo(string connectionString)
    {

    }

}

Your castle Windsor container will look like as follows (i.e. adding facility):

IWindsorContainer container = new WindsorContainer();
        ///Register your ConfigProvider here
        container.AddFacility<DependenciesFromAppSettings>();
        container.Register(Component.For<MyRepo>());

Note the code is borrowed from above link.

Hope this helps