How to configure Autofac on WindowsPhone7

Getting setup

While I have been off spending time with WindowsPhoneMVP, Autofac and other various projects, it seems that there is no real guides around for quickly configuring Autofac in an existing Windows Phone 7 application, even if it may be using the "service locator" pattern. Although I generally disagree with this approach, it does allow the use of Blend which is essential for phone developers.

So to start playing around with this, I'm going to be using the standard out of the box template, and create a new "Windows Phone Panorama Application", then nuget Autofac.

Now that the hard part is done :P we can start to introduce the container to our project. In the App.xaml.cs file, I usually end up adding these things:

//Add the container
private IContainer _container;

public App()
{
    UnhandledException += Application_UnhandledException;
    InitializeComponent();
    InitializePhoneApplication();

    //Add the Exit event
    Exit += AppExit;
}

private void InitializePhoneApplication()
{
    if(phoneApplicationInitialized)
        return;

    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    //Initialize the container
    _container = Bootstrap();

    phoneApplicationInitialized = true;
}

//Clean up when the application exits
void AppExit(object sender, System.EventArgs e)
{
    Exit -= AppExit;
    if(_container != null)
        _container.Dispose();
}

//Our bootstrapping code, create a container builder and add in our modules
private IContainer Bootstrap()
{
    var builder = new ContainerBuilder();
    builder.RegisterInstance(RootFrame).SingleInstance();
    builder.RegisterModule(new ClientModule());
    return builder.Build();
}

At this point, if you are comfortable with continuing on yourself and configuring Autofac then great. If you're looking for some more ideas around how we can implement Lifetime scopes, read on.

Imagine that we have a situation as demonstrated below. In this situation, we have a Main view, then have navigated to a second view. My ideal is that each View would be encapsulated in its own Lifetime scope. The reason for this is that when the user navigates back in the application, we tell Autofac to release that lifetime scope, and with it releasing all unneeded resources. However you also can notice that there is a shared component, this might be an example of a something that has registered as SingleInstance(). So because our container is managing component lifetime and not the ViewModels (or the App.xaml.cs), then we get the power to use component registrations to specify if the service should be Transient, Singleton or PerLifetimeScope. This is really where we start to Leapfrog over other so called IoC containers for Windows Phone and gain some real control over our component construction and lifetime.

alt Lifetime Scope Example

Implementing Lifetime Scopes

Fortunately there are some examples that help you implement Autofac and setup Lifetime scopes on Windows Phone. The project I have been working on called Windows Phone MVP comes ready to go with Autofac and Project Templates that already have the ground work done. However I realise that the MVP pattern doesn't suite everyone. Another project that will be available soon is Windows Phone MVC, this will have an extension for Autofac that will setup and teardown Controllers in all the right places (stay tuned).

So how can we do this with the Service Locator pattern, well I have to admit that I don't feel its as nice of a solution as the others, but it is definitely possible. To start I went on to create a few more infrastructure classes, I have introduced an IViewModelFactory and AutofacViewModelFactory.

alt More infrastructure

So as we now have a ViewModelLocator, I set it up the way you might normally, by defining the static instance in the App.xaml.cs file.

<!--Application Resources-->
<Application.Resources>
    <Infrastructure:ViewModelLocator x:Key="Locator" />
</Application.Resources>

Then I go back to my App.xaml.cs file and assign my AutofacViewModelFactory to that instance.

private void InitializePhoneApplication()
{
    if(phoneApplicationInitialized)
        return;

    RootFrame = new PhoneApplicationFrame();
    RootFrame.Navigated += CompleteInitializePhoneApplication;
    RootFrame.NavigationFailed += RootFrame_NavigationFailed;

    _container = Bootstrap();
    ((ViewModelLocator) Resources["Locator"]).Factory = new AutofacViewModelFactory(_container);

    phoneApplicationInitialized = true;
}

I'm then able to use that to bind to my DataContext in my MainPage.xaml by using the the DataContext Attribute.

DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"

And that's it, normal usage of the ServiceLocator, however the AutofacViewModelFactory is automatically setting up and tearing down the lifetime scopes when you navigate back from a View. Here is that magic.

using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Navigation;
using Autofac;
using Microsoft.Phone.Controls;

namespace WindowsPhonePanoramaApplicationAutofac.Infrastructure
{
    public class AutofacViewModelFactory : IViewModelFactory
    {
        readonly IContainer _container;

        readonly IDictionary<object , ILifetimeScope> _viewsToContainers = new Dictionary<object, ILifetimeScope>();
        readonly object _viewModelToContainersSyncLock = new object();
        private readonly PhoneApplicationFrame _frame;

        public AutofacViewModelFactory(IContainer container)
        {
            _container = container;
            _frame = _container.Resolve<PhoneApplicationFrame>();
            _frame.Navigating += FrameNavigating;
        }

        public T Create<T>()
        {
            var requestScope = _container.BeginLifetimeScope(builder => 
                    builder.RegisterType(typeof(T)).AsSelf()
                );
            var viewModel = requestScope.Resolve<T>();

            lock(_viewModelToContainersSyncLock)
            {
                _viewsToContainers[viewModel] = requestScope;
            }

            return viewModel;
        }

        public void Release(object viewModel)
        {
            var viewsToContainer = _viewsToContainers[viewModel];
            lock(_viewModelToContainersSyncLock)
            {
                _viewsToContainers.Remove(viewModel);
            }

            // Disposing the container will dispose any of the components
            // created within it's lifetime scope.
            viewsToContainer.Dispose();
        }

        void FrameNavigating(object sender, NavigatingCancelEventArgs e)
        {
            if(e.NavigationMode == NavigationMode.Back)
            {
                if(e.Uri.ToString() == "app://external/" || e.Cancel)
                    return;

                var currentView = GetContentPage();
                if(currentView != null && currentView.DataContext != null)
                {
                    Debug.WriteLine(string.Format("Releasing scope for {0}", currentView.DataContext.GetType().Name));
                    Release(currentView.DataContext);
                }
            }
        }

        private Control GetContentPage()
        {
            Control content = null;
            if(_frame != null && _frame.Content != null && (_frame.Content is Control))
                content = (Control)_frame.Content;
            return content;
        }
    }
}

Summary

So in this relatively short post, we've covered starting a new project, fetching Autofac, setting up and basics and even implementing lifetime scopes on a per view basis. When developing phone applications I certainly appreciate this kind of flexibility and power when using a container. There is so much more these days to containers than a simply little dictionary of types that can push a few instances into a constructor. If you are familiar with Autofac, you'll have all the power of component discovery, delegate factories and most other things supported by the desktop version. A few marketplace published apps already using Autofac are MahTweets mobile and Mobifit, if you know of others please feel free to plug them below :)

Downloads

WP7 Autofac
Posted by: Brendan Kowitz
Last revised: 21 Sep 2013 12:13PM

Comments

No comments yet. Be the first!

No new comments are allowed on this post.