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:
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:
- Determining the appropriate IResult to create.
- 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.
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:
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:
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:
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
Finally section 5 registers our
ITypedFactoryComponentSelector to be
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.