Castle Windsor Typed Factory: Resolving dependencies at runtime


Learning about how to use IoC containers, correctly, is quite challenging and while I am still by no means an expert, I am getting a better grasp of how to do it the “proper” way. On my journey, I have encountered the need to resolve a dependency at run time rather than at compile time. So, consider the following code:

public IResult Parse(string[] args)
{
var argumentOption = new ArgumentOption(_dataModelBinder);
var boundArgumentOption = argumentOption.Bind(args);
var bindingResults = boundArgumentOption.Validate(_argumentOptionValidator);
if (bindingResults.Any())
{
return new ErrorResult();
}
return new CreateReportResult(
_resultActioner
, boundArgumentOption.OutputFilePath
, boundArgumentOption.PatientId
, "database");
}

Although this code works as is, there is unfortunately a strong coupling of the IResult which is brought about by instantiating either “ErrorResult” or “CreateReportResult” depending upon the value of “bindingResults”.

The way to deal with this is to use an Abstract Factory i.e. create an abstraction that will resolve a dependency at run time as explained in this blog post. At the end of the latter, there is a mention about how to use Castle Windsor’s typed factory facility, to automagically create these abstract factories. I was having some difficulty actually using the typed factory facility and hence why I am blogging about this.

The main issues

There were two main issues:

  1. Determining the appropriate IResult to create.
  2. Passing the primitive type dependencies required in the instatiation of the CreateReportResult constructor which has the following signature:
public CreateReportResult(IResultActioner resultActioner, Uri filePath, string patientId, string dataSource)

in particular filePath, patientId and dataSource whose values are determined at runtime.

The solution

So here is how I ended up resolving these problems. First, as per the Castle Windsor documentation, we need to create the abstract factory interface definition:

public interface IResultFactory
{
IResult Create(int numOfErrors, Uri filePath, string patientId, string dataSource);
}

The numOfErrors argument is used to address issue 1 above i.e. determining whether to instantiate an ErrorResult or a CreateReportResult. The other arguments will be used in the creation of the latter.

You don’t need to provide an implementation of the interface as Castle Windsor will take care of that under the covers.

This means that the Parse method can now be changed to:

public IResult Parse(string[] args)
{
var argumentOption = new ArgumentOption(_dataModelBinder);
var boundArgumentOption = argumentOption.Bind(args);
var bindingResults = boundArgumentOption.Validate(_argumentOptionValidator);
return _factory.Create(bindingResults.Count()
, boundArgumentOption.OutputFilePath
, boundArgumentOption.PatientId
, "database");
}
view raw ParseIoC.cs hosted with ❤ by GitHub

Then in my IWindsorInstaller implementation I add the following registration code:

//1
container.AddFacility<TypedFactoryFacility>(); 

//2
container.Register(
Component
    .For<IResult>()
    .ImplementedBy<ErrorResult>()
    .Named("ErrorResult")
    );

//3
container.Register(
Component
    .For<IResult>()
    .ImplementedBy<CreateReportResult>()
    .Named("CreateReportResult")
    );

Section 1 is required because we need to tell Castle Windsor that we want to make use of the typed factory facility. Sections 2 and 3 are simply mapping IResult to ErrorResult and CreateReportResult respectively. So far that should hopefully be very familiar and make sense.

We are inching closer to having Castle Windsor instantiating our dependencies but we still haven’t addressed our two issues outline above. In order to do so, we need to make use of ITypedFactoryComponentSelector. The latter contains the logic of which IResult needs to be instantiated is done:

public class CustomTypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(System.Reflection.MethodInfo method, object[] arguments)
{
if (method.Name == "Create" && arguments.Length == 4 && arguments[0] is int)
{
if (Convert.ToInt32(arguments[0]) > 0)
{
return "ErrorResult";
}
return "CreateReportResult";
}
return base.GetComponentName(method, arguments);
}
}

Finally, we need to complete the depency registration as follows:

//4
container.Register( 
   Component
   .For<IResultFactory>()
   .AsFactory(c => c.SelectedWith(new CustomTypedFactoryComponentSelector())));

//5
container.Register( 
    Component
    .For<ITypedFactoryComponentSelector>()
    .ImplementedBy<CustomTypedFactoryComponentSelector>());

Section 4 is required otherwise Castle Windsor will use the default ITypedFactoryComponentSelector rather than our  CustomTypedFactoryComponentSelector.

Finally section 5 registers our ITypedFactoryComponentSelector to be  CustomTypedFactoryComponentSelector.

This takes care of issue 1. As far as issue 2 is concerned, it is my understanding that the arguments passed into CustomTypedFactoryComponentSelector are automagically wired up when the CreateReportResult is instantiated as long as the same argument names are used in the AbstractFactory interface (IResultFactory) and the constructor method (CreateReportResult).

When it comes time to instantiating a new IResult, Castle Windsor now has enough information to do so and that’s how you decouple runtime dependencies.

I will end this post by extending my thanks to Martin who replied to my question on StackOverflow.

Posted in Castle.Windsor, IoC
One comment on “Castle Windsor Typed Factory: Resolving dependencies at runtime
  1. Rodrigo says:

    I came here from your question on StackOverflow. Thanks a bunch for blogging this!

Leave a comment