Creating a Xamarin Forms App Part 5 : Dependency Injection

  • Part 1 : Introduction
  • Part 2 : Getting Started
  • Part 3 : How to use Xamarin Forms with Visual Studio without the Business Edition
  • Part 4 : Application Resources
  • Part 5 : Dependency Injection
  • Part 6 : View Model First Navigation
  • Part 7 : Unit Testing
  • Part 8 : Consuming a RESTful Web Service
  • Part 9 : Working with Alerts and Dialogs
  • Part 10 : Designing and Developing the User Interface
  • Part 11 : Updating to Xamarin Forms 1.3
  • Part 12 : Extending the User Interface

Why Dependency Injection?

Dependency Injection (DI) is a fundamental practice used to manage the dependences between application objects in medium to large-scale applications. Indeed small applications will also benefit from it. However, it is one of the most misused and misunderstood patterns around today. DI allows us to remove the responsibility of creating objects by those objects that are dependent on them. Indeed many of these objects may be shared by other objects and may also have dependencies of their own. This can cause a situation where you end up having to pass down objects deep into an object graph just so they can be use by an object further down. We can simply this by having a container where objects can be registered and resolved. This is known as the inversion of control (IoC). i.e. Control for creating objects has been inverted so they are created by an external source and then passed to objects. I don’t want to go to deep into this. There are plenty of resources out there on DI and IoC and I urge you to read up on this subject if you are unfamiliar with it.

Service Locator – The Anti-Pattern

There is also another pattern that is related to Dependency Injection called Service Location. This pattern also makes use of an IoC container to resolve objects and in some cases their dependencies. It is generally a static class that can be used within objects themselves to resolve objects they wish to use. This breaks the fundamental principle of DI where the responsibility for resolving objects should be completely removed from objects and any dependencies should be ‘injected’ into them. Service Location is actually an anti-pattern as it creates more problems than it solves. Using a service locator to resolve dependencies from within an object means that you are hiding the fact that there is a dependency. This has very undesirable consequences when you come to use that object then discover it throws an exception because one of it’s dependencies could not be found. There is no way of knowing without having an understanding of the internals of object, and which objects must be registered with the Service Locator container before you can make use of it.

The correct approach should be for its dependencies to be clearly advertised by its constructor. This then demands those dependencies are passed as parameters on order for the object to be created. This also helps enormously with unit testing. Without knowing of an object’s dependencies it is very likely that your tests are going to fail miserably. By taking the right approach and having those dependencies passed in to the constructor as interfaces we can mock the dependency objects in our tests. For simple applications this maybe fine, but generally I would advise against any form of service locator. More can be found on Service Locator as an Anti Pattern here.

 Xamarin Forms Dependency Service

Xamarin Forms provides an IoC container out of the box called DependencyService. Unfortunately this is not a Dependency Injection Container as the name suggests. It is a very simple Service Locator that allows us to register different implementations of an interface for each target platform so that we can make use of the correct one depending on the platform. It’s great that the Xamarin Forms team thought to include this but unfortunately it suffers from the same Service Location problems. Further more, it also makes use of a Dependency attribute that must be placed above the implementing class. This tightly couples that implementation to the DI framework being used. This is a bad thing. As your application grows it may be appropriate to switch to a better DI container. You’re then going to have to go through every implementing class and remove all those attributes and DependencyService calls. Ok, no big deal you might think, but better to be avoided. So I would not recommend using the built in DependencyService and instead seek to use one of the many readily available DI frameworks that are available through NuGet.

Which DI should I use?

There are already many mono versions available through NuGet. Personally I have used Unity and Autofac, but you can chose others if you feel more comfortable with them. However, there is another very important aspect to DI that you should consider, and as I said earlier is the cause of much DI misuse. Let’s take Unity as an example. Unity works well if used correctly, but it also suffers from what I’d describe as a DI/ServiceLocator hybrid. It allows you to pass the container around in your application so that it can be used from within your classes to resolve its dependencies. A classic example of this is where you have a class that has a collection that it needs to add items to. And those items themselves have dependencies of their own. The number of those items is dynamic and can only be determined at runtime. I have so often seen the DI container passed to the parent object as a dependency so that it can be used to resolve its child items at runtime. The DI container has now become a Service Locator. This however is unnecessary and can be dealt with in a much more elegant way. Some DI frameworks do this by enforcing that the container cannot be accessed once you have registered and built it.

One such DI framework is Autofac which takes a much more pure approach to DI than Unity. It also, in my opinion, has a much cleaner fluent API to work with. So how does it deal with the example above and create objects that need to be resolved within a parent object at runtime.

The answer lies in it’s name Autofac (Auto Factory), and the use of Func<Tin…, Tout>. This acts as an auto dependency resolver factory. All you need to do is declare a Func<> constructor argument where the out type is the type of the object you wish to resolve, and in types can also be defined if the dependency object requires parameters that are not registered with the container. (e.g. a Model object). We can declare a constructor something like this.

public ParentViewModel(Func<ItemModel, ItemViewModel> itemFactory)

And use it like this.

itemFactory(model);

The beauty of this is that it is now clear from the constructor that the ParentViewModel requires some function that expects an ItemModel and returns an ItemViewModel.

I have therefore chosen to use Autofac for Dependency Injection in my application. Autofac is also surprisingly very lightweight which makes it suitable for mobile development and can be easily added via NuGet. For more information about Autofac please take a look at the documentation here.

So, let’s get started.

I am going to change the main page so that it displays a list of mountain areas. These mountain areas are going to come from a service, which later will get its data from a web service. But for now we are just going to hard code some data to make things simple.

Adding Autofac

From the applications shared PCL project (Silkweb.Mobile.MountainForecast) choose Add Packages from the Packages folder, search for AutoFac and then add the package to the project.

0

Autofac supports defining your DI configuration in Module classes which keeps the registration of objects nicely encapsulated. More on this here.

Create a class called MountainForecastModule in the root of the project that inherits from Autofac.Module and overrides the Load method.

using System;
using Autofac;
 
namespace Silkweb.Mobile.MountainForecast
{
    public class MountainForecastModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
        }
    }
}

This method is where we will place the registration configuration. At the moment we have nothing really to register so let’s focus on getting our main page to display a list of mountain areas.

Adding a Service

Let’s create our model. Create a class in the models folder to represent a location.

public class Location
{
    public int Id { get; set; }
 
    public string Name { get; set; }
}

Create a Services Folder and add the following interface and implementation for our MountainWeatherService, which contains one simple method to return a list of Mountain Areas.

    public interface IMountainWeatherService
    {
        IEnumerable<Location> GetAreas();
    }

    public class MountainWeatherService : IMountainWeatherService
    {
        public IEnumerable<Location> GetAreas()
        {
            return new Location[]
            {
                new Location { Id = 100, Name = "Area 1" },
                new Location { Id = 101, Name = "Area 2" },
                new Location { Id = 102, Name = "Area 3" },
                new Location { Id = 103, Name = "Area 4" },
                new Location { Id = 104, Name = "Area 5" },
            };
        }
    }

Let’s create a View Model to represent this page. In the ViewModels folder add the following View Models.

public class MountainAreaViewModel
{
    private IMountainWeatherService _mountainWeatherService;
 
    public MountainAreaViewModel(Location location, 
        IMountainWeatherService mountainWeatherService)
    {
        _mountainWeatherService = mountainWeatherService;
        Name = location.Name;
    }
 
    public string Name { get; set; }
}
 
public class MountainAreasViewModel
{
    private readonly IMountainWeatherService _mountainWeatherService;
    private readonly Func<Location, MountainAreaViewModel> _areaViewModelFactory;
 
    public MountainAreasViewModel(IMountainWeatherService mountainWeatherService, 
        Func<Location, MountainAreaViewModel> areaViewModelFactory)
    {
        _areaViewModelFactory = areaViewModelFactory;
        _mountainWeatherService = mountainWeatherService;
        Title = "Mountain Areas";
        GetAreas();
    }
 
    public string Title { get; set; }
 
    public IEnumerable<MountainAreaViewModel> Areas { get; set; }
 
    private void GetAreas()
    {
        var areas = _mountainWeatherService.GetAreas();
        Areas = areas.Select(location => _areaViewModelFactory(location))
            .ToList();
    }
}

MountainAreaViewModel represents a single Mountain Area and currently just exposes it’s Name. MountainAreasViewModel represents the list of areas and exposes these through the Areas property. Notice that both constructors expect an instance of IMountainWeatherService.

The MountainAreasViewModel uses this to get the list of mountain areas. MountainAreaViewModel currently doesn’t use the service but it will do later and I want to show you how we can pass this to both view models using dependency injection. Also notice that MountainAreasViewModel has a second parameter defined as:

Func<Location, MountainAreaViewModel> areaViewModelFactory

Now let’s create the actual view. In the Views folder create a new Forms ContainPage called MountainAreasView.xaml and add the following xaml.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:Class="Silkweb.Mobile.MountainForecast.Views.MountainAreasView"
    Title="{Binding Title}">

    <ListView ItemsSource="{Binding Areas}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding Name}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Here we are simply showing a ListView that is bound to the Areas property with a DataTemplate that displays the Name property for each mountain area. I have also bound the Title of the page to the Title property on the view model.

In the code behind of the view add the following to the constructor.

public MountainAreasView (MountainAreasViewModel viewModel)
{
    InitializeComponent ();
 
    BindingContext = viewModel;
}

This enforces that the view expects a MountainAreasViewModel as a constructor parameter which it then uses to set as its BindingContext.

Now let’s hook all this together with Autofac. Return to the MountainForecastModule we created earlier and add this following registration code.

protected override void Load(ContainerBuilder builder)
{
    builder.RegisterType<MountainWeatherService>()
        .As<IMountainWeatherService>()
        .SingleInstance();
 
    builder.RegisterType<MountainAreaViewModel>();
 
    builder.RegisterType<MountainAreasViewModel>()
        .SingleInstance();
 
    builder.RegisterType<MountainAreasView>()
        .SingleInstance();
}

Notice here we are registering the service against its interface and specifying it as a single instance. We have also registered the the view and view model types. Both MountainAreasViewModel and MountainAreasView are registered as singleton instances because we only need one instance. We will however require multiple instances of MountainAreaViewModel.

Finally in the MountianForecastApp class update the constructor to create the Autofac container and set the MainPage like this.

public MountainForecastApp()
{
    InitializeComponent();
 
    var builder = new ContainerBuilder();
    builder.RegisterModule<MountainForecastModule>();
    var container = builder.Build();
 
    var page = container.Resolve<MountainAreasView>();
    MainPage = new NavigationPage(page);
}

Notice here that we are registering the MountainForecastModule with the ContainerBuilder and then calling build to build and return our container. We then use the container to resolve the MountainAreasView and set the MainPage to a NavigationPage with it’s root set to the view. Also delete MainPage.xaml and MainPageViewModel as we no longer need these.

Now build and run the App in the iOS simulator.

1

Everything works!

MountainWeatherService gets injected into both ViewModels. The MountainAreasViewModel gets injected with the factory function for creating its area items and the AreaViewModel get’s injected with the Location instance it needs to display.

You may be thinking that all this seems a little overly complex just to show this page and pass a couple of parameters to a constructor. This may be true right now, but as I build out this App things will get a bit more complex. What I have set up here will make everything much simpler and easier to extend.

There are a couple of things that I’m not happy about though.

I don’t really like how I’m bootstrapping the Autofac container within the App class. I would prefer to have a separate Bootsrapper class for this task. Let’s fix that. Delete the code in the MountainForecastApp constructor and create a class called Bootstrapper with the following code.

public class Bootstrapper
{
    public static void Run()
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<MountainForecastModule>();
        var container = builder.Build();
 
        var page = container.Resolve<MountainAreasView>();
        MountainForecastApp.Current.MainPage = new NavigationPage(page);
    }
}

Update the MountainForecastApp constructor to run the Bootstrapper.

public MountainForecastApp()
{
    InitializeComponent();
    Bootstrapper.Run();
}

This keeps the App class nice and clean and delegates the bootstrapping to the Bootstrapper class. This will yield greater benefits as I build out my App.

I also don’t really like how I’m wiring up my View and View Model by passing in an instance of the View Model to the View. There are better ways to accomplish this, which I will be addressing in my next post.

For now I hope you enjoyed this post and found it useful.