Open Generic Types
Windsor supports registration and resolution of open generic types. An open generic type is a generic class without its type arguments specified. For example, if you have a Repository<T> class, then Repository<Invoice> is a closed generic type because T is specified as Invoice, whereas Repository<> is an open generic type because T is left unspecified.
You can register closed generic types just as you would any other dependency:
container.Register(
Component.For<IRepository<Invoice>>()
.ImplementedBy<Repository<Invoice>>()
);
You could then retrieve this component like this:
var repo = IoC.Resolve<IRepository<Invoice>>();
However, the following would fail because you had not registered a Repository<Customer>:
var repo = IoC.Resolve<IRepository<Customer>>();
Instead of explicitly registering each closed generic type that you want to use, you can instead register the open generic type:
container.Register(
Component.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>))
);
You can now ask the container for IRepository<Invoice>, IRepository<Customer>, IRepository<KitchenSink>, etc. Windsor creates closed generic types on the fly from the open generic type registration!
Notice the slightly different syntax in the call to Component.For(), which passed typeof(IRepository<>). The reason is that generic type arguments (the stuff in angle brackets) in C# accept only closed generic types. If you want to specify an open generic type, you need to use the typeof() syntax.
Author's Note: When you're using Convention-over-Configuration via AllTypes, open generic types are located and registered automatically without you having to worry about the details. This is yet another reason to prefer Convention-over-Configuration where possible. |
Testing Windsor Configuration
You should have integration tests around your components to provide confidence that the container is configured correctly. However, it is often useful to have a smoke test to quickly verify your container configuration. If you have an IWindsorContainer, you can walk through all registered components and ask the container to resolve (and thus create, with all its dependencies) each one:
foreach(var handler in container.Kernel
.GetAssignableHandlers(typeof(object))) {
var impl = handler.ComponentModel.Implementation;
if(impl.IsAbstract || impl.IsGenericTypeDefinition) {
continue;
}
container.Resolve(handler.Service);
}
Notice that you ignore abstract and open generic types. Abstract types, by their very definition, cannot be created. Open generic types could be resolved if you supplied appropriate generic constraints, but unless you're using many open generic types, it is often easier to skip them and add additional logic to explicitly resolve them with an appropriate type parameter, such as an explicit call to container.Resolve<IRepository<Invoice>>(). You might also want to skip over Presenters or similar components that require external dependencies and write Presenter-specific smoke tests yourself. Still, the simple test above will verify the registrations of the majority of your components.
I hope that you have found this brief tour of Castle Windsor informative and have a better understanding of how you can use it for dependency registration and resolution. IoC containers such as Windsor provide a solid foundation for keeping your application architecture flexible and robust in the face of ever-changing requirements.