2
votes

Some of my classes have a constructor similar to the following:

public class MyComponent : BaseComponent, IMyComponent
{
    public MyComponent(IPostRepository postsRepo, int postId, ICollection<string> fileNames)
    {
        // ...
    }
}

IPostRepository is a volatile dependency, but it can be initialized at application start. The postId and fileNames arguments are only known at run time.

How can I use Castle Windsor (3.2.0, if it matters) to handle the injection of the IPostRepository dependency while still allowing run-time constructor parameters?

(While one approach might be to refactor MyComponent, this would be a significant undertaking as many other portions of the code already refer to MyComponent.)

Here's where I've gotten so far: I think that I need to create a MyComponentFactory. The interface of MyComponentFactory would look like

public interface IMyComponentFactory
{
    IMyComponent Create(params object[] args);
}

This IMyComponentFactory would be injected into the layer above (the controller in my case) like so:

public class MyController : Controller
{
    private IMyComponentFactory _myComponentFactory;

    public MyController(IMyComponentFactory myComponentFactory)
    {
        _myComponentFactory = myComponentFactory;
    }

    public ActionResult MyAction(int postId)
    {
        List<string> fileNames = new List<string>();
        // ...

        // Creates a new instance of the resolved IMyComponent with the IPostRepository that was injected into IMyComponentFactory and the run time parameters.
        IMyComponent myComponent = _myComponentFactory.Create(postId, fileNames); 

        // Now do stuff with myComponent

        return View();
    }
}

Finally, I have attempted to let Castle Windsor create the factory implementation by registering my IMyComponentFactory in the composition root like so:

// Add Factory facility
container.AddFacility<TypedFactoryFacility>();

container.Register(Component.For<IMyComponentFactory>().AsFactory());

Doing this results in a DependencyResolverException with a message of

Could not resolve non-optional dependency for 'Playground.Examples.Components.MyComponent' (Playground.Examples.Components.MyComponent). Parameter 'postId' type 'System.Int32'

The error makes sense, and I'm guessing that I need to create a custom implementation of IMyComponentFactory, but I'm not sure how to go about it.

2

2 Answers

1
votes

Why can't you do something like the following:

public class MyComponentFactory : IMyComponentFactory
{
    private IPostRepository postRepository;

    public MyComponentFactory(IPostRepository postRepository)
    {
       this.postRepository = postRepository;
    }

    public IMyComponent Create(int postId, ICollection<string> fileNames)
    {            
        return new MyComponent(this.postRepository, postId, fileNames);
    }
}

I would use explicit parameters in your Create method.

Then register MyComponentFactory against the interface IMyComponentFactory (as a singleton)

0
votes

Just declare strongly typed parameters of Create factory method:

public interface IMyComponentFactory
{
    IMyComponent Create(int postId, ICollection<string> fileNames);
}

And don't forget to register your component:

Component.For<IMyComponent>().ImplementedBy<MyComponent>().LifestyleTransitional()