Prism from Microsoft Patterns & Practices leans on an Inversion of Control (IoC) container. While nominally indifferent to your choice of container, you have to work a bit to extricate yourself from its default choice which is Microsoft Unity. More so with some containers than others.
The sweat and tears flow in the “Bootstrapper” class with which you launch your application. Most Prism samples feature a Bootstrapper that inherits from “UnityBootstrapper”. “UnityBootstrapper” busies itself with registering and configuring a dozen or so Prism components in a manner characteristic of Unity and it unapologetically takes a hard dependence on Unity itself. You are welcome to write your own Bootstrapper base class from scratch; of course this entails reading and understanding what “UnityBootstrapper” does so you can reassure yourself that your replacement achieves the same effects. Who has time for that?
Almost a year ago I found myself writing a single application with three different client technologies, each dependent upon its own container. The three diverged in minor details – each was a different incarnation of a Container + ObjectBuilder – but the differences were sufficiently great that I felt compelled to unleash the Prism base bootstrapper class from its Unity chains.
Preoccupied with more important business, I took the most expeditious route. I salvaged as much of Prism’s UnityBootstrapper as I could while replacing its direct dependency on Unity library references.
Prism initialization is not something I want to own. When P&P updates its UnityBootstrapper, I want a quick, no-thought pathway to update my IoC-agnostic version.
The result was Cal.Bootstrapper (“CAL” is short for “Composite Application Library”, Prism’s official name) and some supporting types. Cal.Bootstrapper looks like a cleaned-up UnityBootstrapper. It goes through the same motions of registering types and configuring instances albeit with reference to a denuded IoC, shorn of all but the most minimal functionality. This, dear reader, will be my gift to you.
Recently, a few folks have wondered about replacing Unity with a completely different IoC container. The one I hear most often is StructureMap. Its author, Jeremy Miller, has several times entertained the notion of replacing “UnityBootstrapper” with something that reflects glory on StructureMap. Apparently he has better things to do. I guess I’ll tackle it.
The Goods
I’ve bundled everything described in this post into a 2 meg zip, PrismRI-Ioc.zip, downloadable from my company website. Do with it as you will.
Please note: I have no intention of maintaining this; don’t even ask.
No, This is Not the Right Way
My knowledge of StructureMap is deeply impoverished but, from the little I do know, I’m certain that the “proper” course is to abandon “UnityBootstrapper” altogether. But there’s a reason Jeremy hasn’t tackled this and no one seems to have done so for other IoC products either.
Nicholas Blumhardt, author of Autofac, took a crack at integrating Prism with his IoC back in August, 2009. He wanted to do it “the right way,” completely rethinking how a Prism app is bootstrapped and how application modules are written. He describes it here as a “work-in-progress”. I guess it took a little more work than he thought it would and he must have found something better to do because it doesn’t look like he finished.
I hoped to avoid this “road to good intentions” by doing it the “the easy way” which may be the “wrong way” but it works … which beats something that doesn’t.
I decided to change as little of the Unity-oriented Prism app approach as I could. I want to get something done. I want you to be able to convert your running Prism app with minimal effort.
Accordingly, my “Cal.Bootstrapper” mimics the “UnityBootstrapper” and it wallows in the style and ceremony that other IoC products despise.
Is it all that bad? Why am I changing my IoC container anyway? I ought to have a clear idea about what I want from an alternative to Unity. I’m wasting my time if I’m only replacing the UnityBootstrapper with something “better”. Frankly, I don’t care how Prism configures its minimal requirements. If I want StructureMap (or AutoFaq, Castle Windsor, Ninject, etc), it is because I prefer that IoC for registering and configuring my own application components. I may replace the Prism configuration eventually but right now that is neither my motivation nor my first step.
Therefore, although my Cal.Bootstrapper is ugly as sin, it will serve me for the nonce as I hope it serves you and I shall proceed to describe it without further apology. I will let it configure Prism the ugly way using my IoC container in place of Unity after which I am free in my own code to drive my IoC container as it was meant to be driven.
Battle Plan
A Prism application has a Bootstrapper class to configure the container, create and configure the main UI view called the “shell”, acquire application modules, and launch the application.
You are expected to write a custom Bootstrapper that inherits from a base Bootstrapper class provided by Prism (the aforementioned UnityBootstrapper). This base class handles the above inventory of start-up tasks. Your derived Bootstrapper is supposed to extend the base class by overriding certain abstract and virtual methods.
In other words, the bootstrapper is launch karaoke. You add your voice at predefined moments. It’s great if the music suits you. If you’re singing off key, you’re should stop faking it and write your own song.
Tempted as I was, I chose not to quarrel with this approach. In fact, I took a deep breath and then embraced it … for the reasons discussed above.
I decided to replace the UnityBoostrapper with an alternative base class (Cal.Bootstrapper) that does not know about Unity.
Your derived Bootstrapper will provide an IoC container to this base class, wrapped in a vendor-neutral “ContainerAdapter, and delivered to the base class by overriding its abstract “CreateContainerAdapter” method.
I have a few additional goals:
- “Improve” the UnityBootstrapper so I can figure out what it does.
- Make it easy to update my base class when (if) Microsoft updates the UnityBootstrapper.
- Minimize the effort to get my existing app bootstrapper deriving fro the IoC-agnostic base class.
- Minimize the changes necessary to convert my existing Unity-based Prism app.
- “Prove it” with an app Prism-aficionados know: the Prism Reference Implementation.
The original UnityBootstrapper is code only a mother could love. Long methods; much repetition; mysterious names. I can’t replace it until I understand what it does. My first step is to refactor it for readability. This is somewhat a matter of taste but I hope you’ll agree that the my revision, called “RefinedUnityBootstrapper”, is an improvement.
I mustn’t get carried away if I am to satisfy my second subsidiary goal. Too many improvements would hinder re-synchronization with Microsoft’s changes.
I want to prove that my approach works on an “application” that anyone can examine and that would be familiar to Prism developers. The Prism RI is the obvious candidate; that’s what I tackled.
I haven’t looked at the RI in a long time. The version on my disk dates from February 2009. That’s pre-Silverlight 3 and, although I had patched it for SL 3, it’s still old and funky. The UI doesn’t work the way it should: stock positions don’t change when you buy and sell, the “Watch List” feature is broken, the Silverlight version throws a recoverable exception when you sell more stock than you own.
But I didn’t have time to freshen it up, it was good enough for my purposes, and it’s all I’m going to give you.
Swappable Versions
This is a tutorial. I want you to be able to switch among different container implementations without changing the body of the application. I don’t have the time or the interest to do anything fancy. In the “real” world, I’d make the change and discard the original.
The version of the RI you run depends entirely upon the executing version of Prism RI Bootstrapper class. The class is divided into two partial class files. One of them is constant. The other holds just the code that varies from container to container. The file with variations is called StockTraderRIBootstrapper.Ioc.Desktop or StockTraderRIBootstrapper.Ioc.Silverlight.
The variations are governed by compiler directives at the top. You uncomment ONE of them, recompile, and let ‘er rip. The example below shows the bootstrapper readied for StructureMap.
//***************************************************************************
// *** PICK YOUR BOOTSTRAPPER AND IOC CONTAINER WITH _ONE_ OF THESE DEFINES
//#define ORIGINAL_UNITYBOOTSTRAPPER
//#define REFINED_UNITYBOOTSTRAPPER
//#define UNITY_CONTAINER_ADAPTER
#define STRUCTUREMAP_CONTAINER_ADAPTER
//#define AUTOFAC_CONTAINER_ADAPTER
//***************************************************************************
Each variation takes only a few lines. I set the title text so you can see which version is running on screen (the original three letter “CFI” company name changes to one of “OUB”, “RUB”, “UCA”, “SMA”, or “ACA” to match your selection). For the Ioc-agnostic variations, there is an override of the method that creates the selected Ioc ContainerAdapter. The two versions pinned to the UnityBootstrapper (remember that I “refined” the UnityBootstrapper first simply to understand it better) have a shim method instead.
Incredibly stupid, I know. I’d never ship anything like this. I live with it because it’s a throw away demo and doesn’t merit the effort or complexity of proper decoupling. Or at least it doesn’t merit that attention yet.
The rest of the Prism RI stays constant. It took some work to keep it that way. Not a lot, fortunately; the Patterns and Practices people did a pretty good job of keeping the RI Ioc-neutral.
I won’t bore you with the details of my journey to this blissful place. They are chronicled in “PrismRIChanges.txt”, located in the PrismRI folder next to the Visual Studio Solution file, “StockTraderRI-IoC.sln”. The gist of it is
- Add the Ioc assemblies in a common directory structure
- Add the PrismBootstrapper solution with its Ioc-agnostic Cal.Bootstrapper and the Ioc-specific ContainerAdapters.
- Revise the start-up project, StockTraderRI, to access the above
- Replace references to “IUnityContainer” with “IContainerAdapter”
- Remove module references to Unity.
You’ll do pretty much the same if you convert your existing Prism application to another Ioc container.
ContainerAdapters
The fun is in the ContainerAdapters which implement the following interface:
namespace Cal.Bootstrapper {
public interface IContainerAdapter {
IContainerAdapter RegisterInstance<T>(T instance);
IContainerAdapter RegisterType<TFrom, TTo>(
ContainerRegistrationScope scope) where TTo : TFrom;
IContainerAdapter RegisterType(
Type fromType, Type toType, ContainerRegistrationScope scope);
IContainerAdapter RegisterTypeIfMissing<TFrom, TTo>(
ContainerRegistrationScope scope) where TTo : TFrom;
IContainerAdapter RegisterTypeIfMissing(
Type fromType, Type toType, ContainerRegistrationScope scope);
T Resolve<T>() where T : class;
object Resolve(Type type);
T TryResolve<T>() where T : class ;
object TryResolve(Type type);
}
public enum ContainerRegistrationScope {
Singleton,
Instance,
}
}
Not so bad if you know your container. Clearly IContainerAdapter must be so dumb that any IoC Container vendor can support it. It needs to be just smart enough for the base Bootstrapper class to configure Prism. Fortunately, this challenge is easily met.
The code tends to be boilerplate. Autofac took a little more work (see below) but still weighs in at only 180 lines.
You also have to write an adaptor that implements Microsoft.Practices.ServiceLocation.IServiceLocator; virtually everyone has done this already.
IoC Containers Demo’d
I’ve tried this approach with three IoC Containers: Unity (Unity v.1.2.0.0, ObjectBuilder2 v.2.2.2.0), StructureMap (v.2.5.4.0), and Autofac (Desktop: v.1.4.5.676, Silverlight: 1.4.4.572).
You should know that I am not an expert in any of these containers. In fact, I just winged it, especially with StructureMap and Autofaq about which I know squat. I figured I could download them, skim whatever documentation they offered, and get something to work adequately for Shmoes like me.
It’s not quite that easy and I have low confidence in my use of these containers. Their authors are invited to lend a hand improving matters … after they’re done laughing.
I wanted to show at least one non-Unity container for both desktop and Silverlight applications. StructureMap doesn’t have a Silverlight version yet (come on, Jeremy). I tried Autofac because it does have a Silverlight version.
I’m glad I did. The StructureMap implementation was a breeze. Autofac was much harder. I’m not trashing Autofac. It just happened to be harder for me, partly due to my ignorance and mostly because of the position Autofac takes on resolving unregistered types.
Major Container “Gotcha”
Container differences matter! One pervasive Prism practice in particular relies on a Unity default behavior that other containers do not observe. It’s serious enough to influence your choice of containers or make your conversion substantially more difficult.
Prism instantiates a lot of its own components with unregistered public concrete classes. For example, somewhere in the bowels of RegionManager initialization, it resolves “DelayedRegionCreationBehavior”. This class is never registered with the container.
Unity is ok with this. When asked to resolve an unregistered public concrete type, it treats the type as if it had been registered and scoped as an instance (i.e., you get a fresh instance each time). Both TryResolve and Resolve behave this same way.
Prism components count on this default Unity container behavior. What if you change containers?
StructureMap has the same default behavior when resolving an unregistered public concrete type. However, it’s TryResolve equivalent, “TryGetInstance”, return null. As I write, I have not detected a consequence of this difference; but I know it’s waiting in the grass somewhere.
You could argue all day about whether a container should return null or return an instance the way Unity does. You could argue whether it should default to Singleton or Instance scope.
Autofac punts. If you attempt to Resolve an unregistered type, it throws an Autofac.ComponentNotRegisteredException with a clear message. “TryResolve” returns null, which is the sensible thing to do.
Aside: the three ContainerAdapter files in my sample implement a “ContainerPlay” class that reveals these differences.
I can’t quarrel with Autofac’s choice but I sure paid the price for purity. I had to hunt down all resolutions of concrete types and manually register them during AutofacContainerAdapter initialization. Unfortunately even that is insufficient. Many class constructors take parameters defined by concrete types. The container will attempt to resolve these and will throw at runtime. There aren’t integration tests to detect these automatically so I have to wander through the app under the debugger. I still haven’t got it to work.
It was easier to get the Prism RI working with StructureMap. I have assumed that the only critical difference is with explicit “TryResolve” calls. I haven’t encountered any optional constructor parameters so I expect StructureMap and Unity to resolve these in the same way.
I searched the Prism and RI source code for “TryResolve” calls involving concrete type. There weren’t many of them. I only found one of potential consequence. The base bootstrapper calls “TryResolve<RegionAdapterMappings>”. Fortunately, it also registers RegionAdapterMappings explicitly so resolution is always successful. Perhaps I was luck. Remember to be careful how you use TryResolve in your application.
The net of it is that containers differ. Prism should not have relied on a Unity-specific type resolution feature. If you have done so in your Unity-based application (as I have), you too are vulnerable.
Where Did The Tests Go?
I removed the test projects from the Prism RI and the Composite Application Library projects that go with it. Why did I do that? laziness?
I really thought this simple task would take maybe a long day at most. Stripping the tests away from the start was supposed to help me stay in the time box. It was going to be non-trivial to setup Prism’s test environment, convert the tests, and repackage for distribution to you. I didn’t think I could afford it.
The point of the exercise was to demonstrate how to liberate your application to use an alternative container. I wasn’t trying to deliver a product and there is limited value in tests for a version of the Prism RI that is (a) obsolete and (b) irrelevant to your application.
Unfortunately, this “simple” task kept expanding in scope; it took four long days. Geez like that never happens.
Now that I’m “done” I wish I had those tests back, the UnityBootstrapperFixture in particular. Debugger testing is painful as we all know. Had I committed to automated tests, I would have written something better than the idiotic compiler-directive-based approach to swapping containers. Sure it would have cost me something – a day? – to get it right, a day I didn’t want to spend. But if someone wants to take this further, the tests would accelerate his progress greatly and imbued his efforts with confidence. Mea culpa.
I wonder if some kind soul will restore them?
Happy New Year, everyone!
6 comments:
Ward,
Nice work. I did something similar for Ninject a while ago. I actually started with a project from Ninject-Contrib that was working for Ninject 1.0 and updated the code to work with 1.5 and later to 2.0. I tried to submit this as a patch and post it to the Ninject mailing list, but apparently there wasn't a lot of interest from anyone.
It wasn't as hard, probably because I'm really familiar with Ninject.
The hardest part was to understand what the UnityContainer was using to know what the Hook Methods should do. A frequent problem with the Adapter Pattern is when you have a lot of interralated hook methods where the results from method1 are important in order to execute method 2. Then it becomes important to know the order where your methods are called to satisfy the requirements of the clients using which IMO is completely wrong. When you implement the abstract method you shouldn't care about the rest a method like CreateContainer should only do that and nothing more.
I don't remember the details know, but I remember that we needed to set some values in some particular methods and there were some ugly dependencies between methods
There's a CommonServiceLocator. I think it would be nice to have a BaseClass that does all the 'common' configuration using the ICommonServiceLocator interface and all it has is an abstract method to create the container. Then in your code you use your specific container, but the bootstrapper doesn't have to know about it.
This might not solve the problem that you have between the different behaviors of IoCs and you might still have to do some manual configuration.
BTW. I don't think that having a dependy to a specific IoC is a "vulnerability" we should be concerned about. It certainly is a problem for a generic framework like Prism, but that's a dependency I'm willing to take for my app.
That said, I think P&P could've done it easier or probably provide some examples with at least one more IoC instead of only working with theirs, but they at least made it possible to swap it.
Miguel - Sorry to hear you can't get more love for your Ninject bootstrapper. Maybe it belongs on the Prism-contrib. You should get it listed on the Prism site at least.
Hard to say what will happen to the popularity of IoC products. I wonder whether Unity will drive others to extinction.
The IoC authors haven't given up. Jeremy is still plugging away at StructureMap. Nick's Autofac v.2 is in beta. MEF is a wild card.
You are right, "the hardest part was to understand ... the UnityContainer". I wrote "RefinedUnityContainer" for the sole purpose of improving that understanding.
Agree with you too about the dangers of the bootstrapper inheritance model. So many hooks; so many places to go wrong.
My CalBootstrapper doesn't change that. But it does give you a single point of change for your IoC container choice ... the CreateContainerAdapter method.
I don't know that referencing the ServiceLocator instead of the container would help much. The base bootstrapper is doing a lot of type registration and not that much type resolution.
I completely agree with you that your application can and should take a dependency on its IoC. Once you've settled on Unity, or StructureMap or Autofac or Ninject, you live with it. It's like picking your database. You're unlikely to swap SQL Server for Oracle; programming as if that were probable is foolhardy.
Frameworks such as Prism, on the other hand, have to keep these options open.
Of course, you want to minimize every dependency when you can easily do so. In this context, it's worth remembering that your application components should only know about the container when they are doing container registration. They shouldn't be injecting the container all over the place (as we often see it done).
That's why I made the point about the Prism RI controller; it shouldn't have depended on a container at all. And I think that's the point you were making about the UnityBootstrapper depending too frequently on the container when it could have leaned on the ServiceLocator.
Thanks for your insightful comments.
@Miguel - forgot to mention that I decided to restore the Prism bootstrapper tests and make them work for all of the IoC boostrapping scenarios. I blogged about it here.
Automated tests that check for method order (and these do) give you more confidence that you've got your custom bootstrapper in sync with Prism's expectations.
You're right about the ServiceLocator, not sure what I was thinking.
I always mention other developers that we need to favour injection over Service Location. Every time someone injects the Container just to resolve some dependencies, he's doing it wrong IMHO.
I'm also in favour of convention over configuration and in most of my projects I tend to resolve my dependencies by convention (e.g. look for a concrete type that matches the interface name in the same namespace, if the type ends with ViewModel add this particular configuration, use this Provider for Interfaces decorated with the ServiceContractAttribute, etc).
In one of those project, due a non-technical deceision, we swithced from Ninject to Unity. The change was super easy in the server where we were using convention and in the client side it was a bit harder where we were using Prism and doing some configuration in the modules it was harder.
Convention also makes it easier to maintain.
About IoCs disappearing. I hope this isn't the case and I seriously doubt that Unity would be the cause of it, but I'm a afraid that people will start using MEF as an IoC instead of using the right tool.
I see a lot of benefits in Ninject and I'm really happy with it. I should probably finish some of my never ending drafts and start blogging again to have something to point you to, but we had a good discussion about Ninject vs Unity here:
http://tinyurl.com/ya462jg
Amiable post and this enter helped me alot in my college assignement. Gratefulness you as your information.
Opulently I agree but I think the list inform should acquire more info then it has.
Post a Comment