Search code examples
c#dependency-injectioncastle-windsor

Can I use child container as factory for component with complex dependencies?


I have a component with multiple dependencies:

class Service
{
    public Service(IDependencyA a, IDependencyB b, ...) { ... }
}

where some dependencies have single implementation:

class CommonDependencyA : IDependencyA 
{ 
    ... 
}

and some have multiple implementations:

class FooDependencyB : IDependencyB 
{
    public FooDependencyB(IDependencyC c, ...) { ... }
}

class BarDependencyB : IDependencyB 
{
    public BarDependencyB(IDependencyC c, ...) { ... }
}

class FooDependencyC : IDependencyC 
{
    ...
}

class BarDependencyC : IDependencyC 
{
    ...
}

Sometimes I want to resolve Service class using "Bar" implementations, while other times I want "Foo" implementations.

I was thinking about using child container to do the registration. This way I could do something like:

public Service CreateService(IWindsorContainer parent, FooBarType type)
{ 
    using(IWindsorContainer child = SetupChildContainer(parent))
    {
        if (type == FooBarType.Foo)
        { 
            child.Register(Component.For<IDependencyC>.ImplementedBy<FooDependencyC>().LifeStyle.Transient);
            child.Register(Component.For<IDependencyB>.ImplementedBy<FooDependencyB>().LifeStyle.Transient);
            ...
        }
        else
        {
           //register "Bar" implementations
        }
        child.Register(Component.For<Service>.LifeStyle.Transient);
        return child.Resolve<Service>();
    }
}

I can then refactor this method into a factory, and register this factory inside the parent container.

What I don't like about this approach is that every time I need to resolve a service, I have to do the registration. I've also read a claim, that using a child container is almost always the wrong solution. So, am I doing it wrong? Is there a better approach? I would rather just register all the components once, then call something like:

var fooService = container.Resolve<Service>(new []{FooBarType.Foo});

and make Widsor figure out the correct dependencies. But I do not know how to do the registration in order for this to work.


Solution

  • I ended up using named dependencies:

    container.Register(Component.For<IDependencyA>().ImplementedBy<CommonDependencyA>());
    container.Register(Component.For<IDependencyC>().ImplementedBy<FooDependencyC>()
                                .Named("FooDependencyC"));
    container.Register(Component.For<IDependencyB>().ImplementedBy<FooDependencyB>()
                                .Named("FooDependencyB")
                                .DependsOn(Dependency.OnComponent(typeof(IDependencyC), "FooDependencyC"));
    container.Register(Component.For<Service>().
                                .Named("FooService")
                                .DependsOn(Dependency.OnComponent(typeof(IDependencyB), "FooDependencyB"));
    

    Not what I was hoping for (I hate using names), but good enough in my case.