In an earlier post this week I wrote about implementing the Model-View-ViewModel pattern in the YouCard application. In this post I’m going to build on that and implement Inversion of Control Containers and the Dependency Injection pattern. Lots have been written about dependency injection, so the focus of this post will be on using dependency injection in a Silverlight context, and specifically how to implement it using the Ninject framework. For an excellent introduction to the pattern I strongly recommend Fowlers essay on the topic.
When getting into dependency injection you might get intimidated by the number of frameworks available to help you implement the pattern. It might give you the impression that dependency injection is something big and complicated, which is going to have a huge impact on how you build your software. The later might be true, but not necessary because of the framework but rather by the flexibility gained by introducing this pattern. In its simplest form dependency injection is about how you supply external dependencies to your application classes. In the previous blog post I refactored the Twitter and Flickr integration into external classes that complies with an interface. Depending on if the code was executed inside Blend or the browser I either use the real implementation, or a mock implementation providing some dummy data at design time.
The problem with the above code is the coupling between the YouCardData class and the four classes implementing the IMicroBlog and IPhotoService interfaces. This high level of coupling between the view-model (YouCardData) and its external dependencies (Twitter and Flickr) makes the code less flexible. Coding against interfaces enables me to implement other services, such as a Picasa photo service, or a Tumblr micro blog service. The problem is how I get the YouCardData class to use these alternative implementations. The code is also hard to unit test as I can’t provide a mock implementation of the external services. We could refactor our code to take the external dependencies as constructor parameters like this:
By doing this we allow the consumer of the YouCardData class to inject the external dependencies through the constructor. And in essence this is constructor based dependency injection, by hand. The above change gives us greater flexibility to provide alternative implementations of the services, either for testing purposes, or to provide new functionality. But there is still a problem with the code. If you want to create an instance of the class from XAML, for instance when setting the data context, a default parameter less constructor will be used. You could solve this limitation by adding an additional constructor like this:
This would solve the XAML problem, as any instance crated declaratively would use the Twitter and Flickr implementation, but for testing purposes we could provide our own mock implementation. But the solution still isn’t perfect. In the first constructor we had code to check if the class was consumed inside Blend or not. Depending on this we used different implementations of the external services. We could have kept this constructor, but instead we’re going to use an Inversion of Control container to help us build our objects.
Lately I’ve been doing some research on UI frameworks and patterns, and many of these use some sort of dependency injection framework. The Prism-AG framework, a Silverlight port of the Composite WPF framework, uses a small dependency injection framework called DLLite. Another dependency injection ported to Silverlight is Unity, another framework from the Microsoft patterns and practices group. But my personal favorite so far is Ninject, the ninja of dependency injection. This framework caught my attention after listening to Nate Kohari in episode 5 and 6 of the AltDotNet podcast. Ninject describes itself the following way:
Ninject is a lightning-fast, ultra-lightweight dependency injector for .NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces, and then glue them back together in a flexible manner. By using Ninject to support your software’s architecture, your code will become easier to write, reuse, test, and modify.
The footprint of the compiled framework is only 110KB, and you only need a single reference to Ninject.Core.dll too getting what you need in most scenarios. Strength of Ninject is the focus of configuration through code instead of XML. Most other IoC containers have a strong emphasis on XML based configuration of object dependencies. This has its advantages if your deployment environment varies a lot between installations, but in many cases this adds additional configuration pain.
- Your configuration can get more complex and verbose since you often have to write out fully qualified assembly names for each type.
- Its easer to break the application through simple typing mistakes.
- If the rules describing how to write your application together gets complex this might be hard to express through XML.
In comparison, Ninject uses a fluent interface, or sometimes referred to as "embedded domain-specific language". This lets you take advantage of the programming language to build up the wiring of your application. You can express complex rules of how the wiring should happen, and there is nothing stopping you from writing code that reads a configuration file if some of the wiring depends on user configuration.
"… There are cases where it’s easier to use program code to do the assembly. One case is where you have a simple application that’s not got a lot of deployment variation. In this case a bit of code can be clearer than separate XML file.
A contrasting case is where the assembly is quite complex, involving conditional steps. Once you start getting close to programming language then XML starts breaking down and it’s better to use a real language that has all the syntax to write a clear program.
…My advice here is to always provide a way to do all configurations easily with a programmatic interface, and then treat separate configuration file as an optional feature. – Martin Fowler"
Another cool aspect of Ninject is that it support Silverlight “out of the box”, and is not a port or a trimmed down version of a larger framework. Ninject was small to begin with.
Dependency Injection with Ninject
Adding Ninject support is fairly straight forward. The first thing we need to do is add is marking all constructors that take the dependencies, and mark it with a simple attribute.
We also want to inject a persistence service into the main page view-model, the one controlling all the cards on screen. The persistence service is used to store all the users added, so that we don’t have to add them again when we come back to the application.
If we use the Twitter service implementation we also want to inject a proxy URL. This is because Twitter doesn’t allow cross-domain callers and we need to proxy the request through our own server.
The next thing we need to do is to wire the view-models to the services they need. In many IoC containers this is done through XML configuration, but in Ninject this is done through code and a fluent interfaces. Ninject uses a concept of modules to wire things together. One kernel (the container) can host multiple modules. For the YouCard application we need only one module.
The benefit of fluent interfaces is increased readability of the code, so I’m not going to spend too much time describing the class line by line. The class derives from StandardModule, and overrides the Load method. In the Load method we set up the binding between the services and the concrete types. If you read the binding as “English”, from left to right, the binding rules should be fairly straight forward. Also worth notice is how I take advantage of configuration through code when I decide which implementation to use when the code is running inside Blend. I add a condition saying that the Twitter service should be used when executing inside the browser, while the mock service should be used from Blend.
To use the module I simply have to create a Ninject Kernel and pass it the module.
Notice how I don’t have to worry about constructing the YouCardData object. The kernel takes care of instantiating the object, and injects any dependency based on the bindings configured in the YouCardModule.
Data binding to objects built by Ninject
Now that we have configured our Ninject kernel the next challenge is getting our view-model objects out of the kernel and into the views, without breaking the design time experience in Blend. Setting the data context through code would be fairly simple. We could create the kernel in the application class, and then get it doing something like this.
That would enable us to have one kernel for the application (something you typically do). But it still wouldn’t solve how to set the data context of a user control to view-model built by Ninject, using declarative XAML code. This troubled me for a few days, and after thinking it through, trying some different approaches, and talking discussing it with Nate Kohari the solution turned out to be Service Locators. This is a different pattern used to implement Inversion of Control, and often you decide to use either service locators or dependency injection. I recommend reading the section on service locators vs. dependency injection in Fowlers essay for a good description of the pattern.
The short version of the pattern is that a service locator is a registry holding references to all the services used by your application. A class, such as the view-models in YouCard, can ask the service locator to give them an implementation of IPhotoService or ITwitterService. The big difference between service locators and dependency injection is that with SL your classes asks for an implementation, but with DI they are given an implementation (pull vs. push). Nate has blogged about how Ninject plays nicely with service locators.
"I don’t think that service locators and dependency injection are mutually exclusive; in fact, I’ve found that it can actually be very effective when used in conjunction with a dependency injection framework like Ninject. – Nate Kohari"
The service locator I’ve created for YouCard is fairly straight forward.
In many cases you implement the service locator as a static class. That was not an option, as Silverlight 2 doesn’t support data binding to static classes. The way I solved this was to make a static constructor, and a private, static kernel field. The static constructor is the first thing executed in the class, before any standard constructors. The static constructor checks if we have a kernel and if not it instantiates one using module containing our binding configuration. By making the kernel field static we can ensure that we have only one instance of it in our application. The class has two instance properties that surface our view-models from the kernel, as well as a generic instance method to get any object from the container.
Before introducing dependency injection we bound our views to the view-model declaratively like this:
After introducing a service locator we need to change this around a little. The first thing we do is to instantiate the service locator as an application level resource:
Then for each of the views we need to change how they get their data context.
Each of the views gets their view-model by data binding to properties on the service locator. The coolest thing about this approach is that we still get full Blend design time support with sample data provided through the mock services.
Introducing dependency injection in Silverlight isn’t any harder than in other .NET applications. The strong focus on keeping the framework light weight and extremely fast makes Ninject a perfect fit for Silverlight. However, one could argue that for a simple example like this Ninject isn’t really necessary. I could hand-write the dependency injection code inside my service locator, and eliminate the Ninject dependency, and still get the flexibility of dependency injection. On the other hand, Ninject is only 110KB (before it’s zipped into the XAP file), and isn’t going to add a lot of download weight to your application. 100KB is less than half the size of the images used as illustrations in this blog post…
By using dependency injection you keep your code more flexible and testable, something that is just as important for Silverlight applications, perhaps even more, are UI requirements are likely to change a lot as you discover the power of Silverlight. I strongly believe in providing designers with a best possible experience inside Blend, and there is no reason for dependency injection to change that.