How Prism supports using multiple IOC containers
Today we received the following question / comment on our forums around our strategy for supporting multiple containers in Prism. This is something that I believe is on the minds of other folks using Prism. Below is a portion of the thread which you can access here
Hi all,
I'm currently getting up to speed with the Composite WPF project and looking to replace Unity with another DI framework (namely Ninject). Initially I had thought that it would just be a matter of duplicating some of the classes in the UnityExtensions project, ie the Bootstrapper and the ContainerAdapter. Indeed this works fine for an example as simple as HelloWorld and things are up and running in no time.
However, when I try to modify StockTraderRI, I notice that the IUnityContainer is being passed around quite a bit - most modules require a reference to it and so do some controllers. For the cases where it's being used to resolve types in the controller and modules, it's acting more as a Service Locator and it feels to me that it would be cleaner to have these dependencies injected instead of explicitly requiring the DI container to resolve them (OrdersController for example). Now this is fairly easy to fix because the dependencies could just be directly injected in the constructor, or if they're not known at construction, then a specific factory could be injected instead of the DI container itself.
The more complicated cases arise in the module initialization where the module needs to register/bind some types in the DI container. I haven't thought too deeply on this and it would be good to get some feedback as to what a clean solution would be so that the DI container is not required to be passed in to each module directly.
One option would be to expand IContainerFacade a little to allow for the registration of types (with singleton options) - this would be quite easy and solves the vendor dependency, but it still smells a little because the entire container facade would still be being passed around to the modules.
Another option may be to create an IModuleInitializer or IModuleRegistrar interface that gets bound during the module registration in the bootstrapper and passed in to the module constructor for use during Initialize (so we'd have, for example, a UnityNewsModuleRegistrar, a WindsorNewsModuleRegistrar, a NinjectNewsModuleRegistrar, etc). As the bootstrapper is already DI container specific, it's less problematic to do any binding here or to have a specific IModuleRegistrar rely on the container. Then, if the module requires certain objects from the DI container after the registration, it could either call something like IModuleRegistrar.Prepare(this) to have properties injected, or possibly cleaner would be to have factories for the required types passed in to the module constructor, which would be in turn initialized by the IModuleRegistrar ready for creating objects when the module needs them.
One question that arises from this approach is, are we just replacing the module with the module registrar? If all the module is responsible for is type registration, then the answer is yes and it's kind of a moot solution - although I still feel the modules should then technically be called UnityNewsModule, UnityPositionModule, etc.
I would argue though that module initialization is probably made up of a number of steps, of which the DI registration is only one - the rest is the logic around adding views to regions, running controllers, etc. If the DI registration step can be factored out into small, DI-specific registrars, then it makes it much easier to swap one DI implementation for another.
So what do you guys think?
Cheers,
Michael
Hi Michael
Your observations are good ones. Supporting multiple IOC containers was an explict goal from the get-go in Prism. However early on we made a decision that doing so doesn't necessarily equate to the "one container to rule them all." We made the decision that "support" means that at the low-level (our 'framework) none of our apis explicitly depend on one container or another. On the other hand, at the high-level within the application we decided we should not have such an abstraction. The reasoning is very simple. Containers have different semantics and syntax.
Part of the reason I choose to use Castle Windsor over Structure Map, NInject, etc is because I like the particular attributes of that container. If I like Structure Map's fluent interface then I want to use that, not some dumbed down abstraction. For this reason we very explicitly use IUnityContainer throughout the RI. We would equally expect if you used NInject then that is what you would use instead. Once I commit to a container, it's unlikely that I would want to change mid-way. However you want to make sure that whatever container you make the decision to use does not block your usage of the patterns and libraries. Now at the core levels, you would need an implementation of IContainerFacade for NInject, and possibly a Bootstrapper if you like the Bootstrapper pattern.
In this way you can use Prism with any container, while at the same time leverage all of the specific benefits each container provides.
As far as the module implementation itself, the reason we inject the container into the module is to allow registration of module-specific types AT load time. This way those types are only registered if the module is loaded. The module is not only used for registering but also instantiating views and controllers. We could have wrapped this with specific services like a ViewService and a ControllerService instead of using the container directly. However, we felt that had the same issues around losing container semantics and such. We could consider this for the future if this is what everyone wants though.
Let us know if this rationale makes sense. Thanks for the feedback!
Comments
- Anonymous
July 01, 2008
PingBack from http://wordnew.acne-reveiw.info/?p=5030