Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to constructor-inject a string that is only known at runtime? (Windsor Castle)

I have class that has dependency on string:

public class Person
{
    private readonly string _name;

    public Person(string name)
    {
        if (name == null) throw new ArgumentNullException("name");
        _name = name;
    }
}

This string 'name' is known only at runtime, eg. it is defined in configuration. So I have this interface that provides this string:

public interface IConfiguration
{
    string Name { get; }
}

Both types, Person and IConfiguration (with its implementation which is not important here) are registered with Windsor container.

Question: how can I tell WindsorCastle container that it should inject the Name property of IConfiguration to the constructor of Person class?

Caveat: I don't want to inject IConfiguration to Person class or use typed factories... the Person class must be simple and accept only string as parameter.

like image 442
matori82 Avatar asked Dec 16 '14 14:12

matori82


1 Answers

There are probably more ways to do this since Windsor is ridiculously flexible, but here are three off the top of my head:

Option 1:

If IConfiguration is a singleton or somehow can populate Name without any other assistance, you could do the following:

container.Register(Component.For<IConfiguration>().ImplementedBy<Configuration>());
container.Register(Component
    .For<Person>()
    .DynamicParameters((DynamicParametersDelegate)ResolvePersonName));

// This should be a private method in your bootstrapper 
void ResolvePersonName(IKernel kernel, IDictionary parameters)
{
    parameters["name"] = kernel.Resolve<IConfiguration>().Name;
}

This method is invoked before resolving the Person, and is setting the name key/value pair to be the desired value. Windsor then uses this dictionary to override any dependencies in the constructor. I'd probably just make it a lambda to be terser:

    .DynamicParameters((k,p) => p["name"] = k.Resolve<IConfiguration>().Name));

Option 2:

If the Name value is actually defined in the applications settings file, Windsor has a built-in option for that:

container.Register(
    Component.For<Person>()
    .DependsOn(Dependency.OnAppSettingsValue("name", "configSettingKeyName")));

Option 3:

You say you don't want to use Typed Factories, but I think this is a reasonable siutation for using one. It wouldn't complicate the Person class at all, but it would add a bit to the code creating the Person objects (people?). Here's an example:

Define the factory interface:

public interface IPersonFactory
{
    Person Create(string name);
}

Then where you are creating your Person, inject the factory and use that instead:

public class PersonUser
{
    public Personuser(IConfiguration configuration, IPersonFactory personFactory)
    {
        Person person = personFactory.Create(configuration.Name);
    }
}

You'll have to add the facility and register the interface, but it's easy:

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IPersonFactory>().AsFactory());
like image 171
Patrick Quirk Avatar answered Oct 27 '22 20:10

Patrick Quirk