Using Ninject with Silverlight to make you code more testable

One useful feature when developing more complex code is using an inversion of control (IOC) container to do dependency injection. The basic principal of inversion of control, or dependency injection, is to pass in any dependencies when an object is created and not create them when needed. This way the code is much more decoupled and much easier to unit test.

Dependency injection with Silverlight

When developing regular .NET code there are plenty of IOC containers to choose from. However when it comes down to Silverlight the choice isn’t quite as big. I am aware of only two IOC containers you can use with Silverlight, Ninject and Unity. In this example I am going to use Ninject but the same approach would be perfectly valid using Unity.

 

The basic application

I am going to use a very basic application to demonstrate using Ninject with Silverlight. Just a TextBlock and Button on the form and when the button is called we are going to call a WCF service and call a ping method returning the current time.

image

The page is real simple. All I do is create a PageModel, make it the DataContext for the form and call the ExecutePing function when the button is clicked.

using System.Windows;
using System.Windows.Controls;
 
namespace NinjectSample
{
    public partial class Page : UserControl
    {
        private PageModel _model;
 
        public Page()
        {
            InitializeComponent();
 
            _model = new PageModel();
            DataContext = _model;
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            _model.ExecutePing();
        }
    }
}


The PageModel class is also very straightforward and looks like this:



using System;
using System.ComponentModel;
using NinjectSample.PingService;
 
namespace NinjectSample
{
    public class PageModel: INotifyPropertyChanged
    {
 
        public DateTime TheResult { get; set; }
 
        public void ExecutePing()
        {
            PingServiceClient proxy = new PingServiceClient();
            proxy.PingCompleted += proxy_PingCompleted;
            proxy.PingAsync();
        }
 
        void proxy_PingCompleted(object sender, PingCompletedEventArgs e)
        {
            TheResult = e.Result;
 
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("TheResult"));
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
    }
}




 



So this works but there is a problem. This code is not very testable as it creates the PingServiceClient, which calls the server, while executing. Lets see how we can refactor this using Ninject to create a bit more maintainable and testable code.



Adding Ninject



First make sure you download the Silverlight version of Ninject from the website here. And add a reference to the DLL’s from the Silverlight project.



image



So lets do the minimum work to use Ninject and have it insert the dependency. Instead of using the new operator to create the PageModel we are going to let Ninject handle this using the following code:



IKernel kernel = new StandardKernel();
_model = kernel.Get<PageModel>();


And to have Ninject inject the dependency on the WCF service we are going to change it to receive he proxy object in the constructor like this:



public class PageModel : INotifyPropertyChanged
{
    public PageModel(PingServiceClient proxy)
    {
        _proxy = proxy;
    }
 
    private PingServiceClient _proxy;
    public DateTime TheResult { get; set; }
 
    public void ExecutePing()
    {
        _proxy.PingCompleted += proxy_PingCompleted;
        _proxy.PingAsync();
    }
 
    void proxy_PingCompleted(object sender, PingCompletedEventArgs e)
    {
        _proxy.PingCompleted -= proxy_PingCompleted;
        TheResult = e.Result;
 
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("TheResult"));
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
}


A few small changes and everything still works exactly as before.



So what did we gain? Only the fact that the dependency between the PageModel and the PingServiceClient is slightly decoupled. However because we are still using concrete classes and the PingAsync() is not virtual we have gained very little.



 



Making the code more testable



In order to make the code more testable we need to start using the PingService interface instead of the PingServiceClient concrete type. So lets make some changes to the code. First of all we need to change the PageModel class to use the interface. This means we can no longer use the PingAsync function and PingCompleted event as they are part of the concrete class. Instead we have to use the BeginPing() and EndPing() functions.



One extra complication here is that the standard generated proxy makes sure the callback event is raised on the right thread, something we now have to take care of using a SynchronizationContext.



The new PageModel looks like this:



using System;
using System.ComponentModel;
using System.Threading;
 
namespace NinjectSample
{
    public class PageModel : INotifyPropertyChanged
    {
        public PageModel(PingService.PingService proxy)
        {
            _proxy = proxy;
            _context = SynchronizationContext.Current;
        }
 
        private PingService.PingService _proxy;
        private SynchronizationContext _context;
        public DateTime TheResult { get; set; }
 
        public void ExecutePing()
        {
            _proxy.BeginPing(proxy_PingCompleted, null);
        }
 
        void proxy_PingCompleted(IAsyncResult result)
        {
            _context.Post(state =>
                {
                    TheResult = _proxy.EndPing(result);
 
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("TheResult"));
                }, null);
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
    }
}


With this in pace we need to configure Ninject a bit so it knows which object to create as the PingService. If we forget to do so we will see a Ninject.Core.ActivationException with the following message:



Error activating PingService: no matching bindings are available, and the type is not self-bindable (or implicit binding is disabled).
Activation path:
  1) active request for PingService

 



Configuring Ninject



The way to configure Ninject is to create a module loader class derived from StandardModule and implement the Load() function specifying the dependencies like this:



using Ninject.Core;
using NinjectSample.PingService;
 
namespace NinjectSample
{
    public class PingModule : StandardModule
    {
 
        public override void Load()
        {
            Bind<PingService.PingService>().To<PingServiceClient>();
        }
    }
}


Next we need to tell Ninject which module to use by passing it into the constructor like this:



public Page()
{
    InitializeComponent();
 
    IKernel kernel = new StandardKernel(new PingModule());
    _model = kernel.Get<PageModel>();
    DataContext = _model;
}


And again everything is working again [:)]



 



Adding a test service



Now we have fully decoupled the pageModel from the service we can create a test service so we execute the code without actually calling the WCF service.



First we need to create a test service like this:



using System;
 
namespace NinjectSample
{
    public class TestPingService : PingService.PingService
    {
        public IAsyncResult BeginPing(AsyncCallback callback, object asyncState)
        {
            callback(null);
            return null;
        }
 
        public DateTime EndPing(IAsyncResult result)
        {
            // Birthdate Charles Babbage
            return new DateTime(1791, 12, 26);
        }
    }
}


The code is simple and just returns a fixed date.



Next we need to tell Ninject which implementation to use. There are several ways to do this. I suppose the best is to create a specific module for testing but in this case I have taken the route of conditionally binding dependencies in a single module like this:



using Ninject.Core;
using NinjectSample.PingService;
 
namespace NinjectSample
{
    public class PingModule : StandardModule
    {
        private bool _testMode;
 
        public PingModule(bool testMode)
        {
            _testMode = testMode;
        }
 
        public override void Load()
        {
            Bind<PingService.PingService>().To<PingServiceClient>()
                .OnlyIf(p => _testMode == false);
            Bind<PingService.PingService>().To<TestPingService>()
                .OnlyIf(p => _testMode == true);
        }
    }
}


Now I can vary the behavior by just passing in true or false when creating the Ninject module.



image



 



Conclusion



Using Ninject, or another IOC container, makes it easier to test code. Of course Ninject isn’t a silver bullet here, the same effect could be achieved with manual injection. But thinking about dependencies this way makes code easier to develop and test. And why do dependency inject by hand if an IOC container like Ninject can do it for you!



Enjoy!



Source code download

4 thoughts on “Using Ninject with Silverlight to make you code more testable

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>