Firstly, sorry it’s been so long since my last MVC+MEF post, been a bit busy with life in general. Still haven’t managed to get my project onto CodePlex, but we shall see soon!

Ok, so where did we get to last time? We’ve managed to build a prototype MVC+MEF framework that supports modular areas and strongly-typed views. A couple of readers have been asking how we can incorporate IoC into our framework, and the question is really: do we need to?

This framework was originally conceived as a starting point for building a web application, so we could essentially allow MEF do handle our DI for us. But its not often the case that we can simply start a new project, but build off an existing project, and to go one stage further, if you have a lot of your existing code infrastructure built around an existing IoC container, then you really need to expose that existing infrastructure to your website.

The good news is there is a way, and it’s rather easy. Glenn Block (everyone’s favourite MEF guy) pointed me to a rather interesting project, called the Common Services Locator, you can find it on CodePlex here.

The CSL project is geared to providing a common abstraction over service location (i.e. accessing and instantiating types, regardless of which third-party IoC container you are using). As long as they provide a CSL provider, we can leverage them relatively easily with out MEF+MVC framework.

So this blog post is essentially going to cover the integration of an IoC container (actually, 2, just to demonstrate how easy it is) into our framework. But before we can do that, we really need to look at refactoring bits of our code. Let’s begin with composition.

Refactoring Composition

In the previous version of the framework, we had all composition taking place within our Application class. This locks both the CompositionContainer, and its associated Catalogs and ExportProviders, hiding them away from the public. If you want to leverage composition in another part of your site, it was exactly easy. So, in steps our new Composer class. This essentially is a wrapper around all the composition functionality of the project, and is something we can easily pass round. Let’s have a look:

/// <summary>
/// Provides a common mechanism for composing parts.
/// </summary>
public class Composer : IComposer
{
  //
}

The Composer wraps up the management of the container, as well as adding any catalogs and export providers. This allows us to easily re-build the catalog as and when we need. When we want to composer a singular instance, we can easily do:

/// <summary>
/// Composes the specified object.
/// </summary>
/// <param name="object">The object to be composed.</param>
public void Compose(object @object)
{
    if (@object == null)
        throw new ArgumentNullException("object");

    if (Catalog == null)
        return;

    EnsureContainer();
    Container.ComposeParts(@object);
}

/// <summary>
/// Ensures the Container has been instantiated/re-instantiated if the Composer has been modified.
/// </summary>
private void EnsureContainer()
{
    if (modified || Container == null)
    {
        if (Container != null)
            Container.Dispose();

        Container = new CompositionContainer(Catalog, ExportProviders.ToArray());

        foreach (var provider in postContainerModifiers.Keys)
            postContainerModifiers[provider](provider, Container);

        if (ExportSelf)
        {
            var batch = new CompositionBatch();
            batch.AddExportedValue<IComposer>(this);
            Container.Compose(batch);
        }

        modified = false;
    }
}

I’ve added an interface, IComposer, which provides a contract by which we can export the Composer itself. This may be useful in scenarios where you want to resolve type instances manually witout worrying about MEF itself. Because of this, the terminology I’ve used for the interface is much more akin to an IoC container, so methods like Resolve and ResolveAll are exposed here. You could then import/inject an instance of the composer if you wanted to:

[ImportingConstructor]
public MyType(IComposer composer) {
  var instance = composer.Resolve<IMyMagicType>();
}

Although really, if you know what types your going to be using, you could just inject them directly instead of using the composer.

We’ve had to refactor the Application class itself to now support the Composer, luckily this wasn’t a lot of work:

#region Properties
/// <summary>
/// Gets the <see cref="Composer" /> used to compose parts.
/// </summary>
public static Composer Composer { get; private set; }
#endregion

#region Methods
/// <summary>
/// The start method of the application.
/// </summary>
protected void Application_Start()
{
    // Perform any tasks required before composition.
    PreCompose();

    // Create the composer used for composition.
    Composer = CreateComposer();

    // Compose the application.
    Compose();

    // Set the controller factory.
    ControllerBuilder.Current.SetControllerFactory(ControllerFactory);

    // Set the view engine that supports imported areas.
    ViewEngines.Engines.Add(new AreaViewEngine());

    // Initialises the application.
    Initialise();

    // Register MVC routes.
    RegisterRoutes();
}

/// <summary>
/// Creates a <see cref="Composer" /> used to compose parts.
/// </summary>
/// <returns></returns>
protected virtual Composer CreateComposer()
{
    var composer = new Composer();

    GetDirectoryCatalogs()
        .ForEach(composer.AddCatalog);

    composer.AddExportProvider(
        new DynamicInstantiationExportProvider(),
        (provider, container) => ((DynamicInstantiationExportProvider)provider).SourceProvider = container);

    return composer;
}

We once again make the CreateComposer method virtual, so if you do want exact control of how the Composer is created, possibly to wire up additional export providers/catalogs etc, you can override this and do what you want.

Refactoring Exports

Currently we manage our exports through a combination of the ExportAttribute and the ExportMetadataAttribute types. This is great, its easy, but does lead to problems, particularly when metadata is missing this is required for composition. This is important, as MEF will not compose types which do not satisfy the required import definitions. Consider our Controller type, currently we see this:

[Export(typeof(IController)), ExportMetadata("Name", "Blog")]
public BlogController: Controller
{

}

If we forget the ExportMetadataAttribute attribute, the controller will not be composed. What we really need to do, is ensure that the controller metadata is passed with the export. We can create a custom export attribute which provides this metadata:

/// <summary>
/// Exports a controller.
/// </summary>
[MetadataAttribute, AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportControllerAttribute : ExportAttribute, IControllerMetadata
{
    #region Constructor
    /// <summary>
    /// Initialises a new instance of the <see cref="ExportControllerAttribute"/> class.
    /// </summary>
    /// <param name="name">The name of the controller.</param>
    public ExportControllerAttribute(string name) : base(typeof(IController))
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentException("Controller name cannot be null or empty.");

        this.Name = name;
    }
    #endregion

    #region Properties
    /// <summary>
    /// Gets the name of the controller.
    /// </summary>
    public string Name { get; private set; }
    #endregion
}

This export attribute ensures that we have the controller name alongside the export. One thing to note, is you don’t actually have to decorate this type with your metadata interface, MEF will project the required properties for you, but I like to do so, as I know it ensures that my export attribute is conforming to my required metadata interface.

Now, we can simply our export:

[ExportController("Blog")]
public BlogController: Controller
{

}

Integrating Unity into MEF+MVC

Ok, finally onto what we really want to do, IoC integration :) . To add support for Unity (and any compatible CSL provider, I’ve added a bit of code supplied by Mr. Block, the CSLExportProvider (read: Glenn’s code). We can augment this ExportProvider into our Composer, and then wire up our Unity container:

public class MvcApplication : Application
{
    #region Fields
    private IUnityContainer unityContainer;
    private CSLExportProvider exportProvider;
    #endregion

    #region Methods
    /// <summary>
    /// Creates the instance of the Unity container.
    /// </summary>
    protected override void PreCompose()
    {
        unityContainer = new UnityContainer();

        var locator = new UnityServiceLocator(unityContainer);
        exportProvider = new CSLExportProvider(locator);

        unityContainer.AddExtension(new CSLExportProviderExtension(exportProvider));

        RegisterTypes();
    }

    /// <summary>
    /// Registers any required types for the Unity container.
    /// </summary>
    protected void RegisterTypes()
    {
        unityContainer.RegisterType<ITicketSystem, SimpleTicketSystem>();
    }

    /// <summary>
    /// Creates the composer used for composition.
    /// </summary>
    /// <returns></returns>
    protected override MefMvcFramework.Composition.Composer CreateComposer()
    {
        var composer = base.CreateComposer();
        composer.AddExportProvider(exportProvider);

        return composer;
    }
    #endregion
}

In the example above, we are taking advantage of our Application class design to control how the Composer is constructed in relation to our IoC Container, Unity. We are then registering a simple example type, the ITicketSystem. Because we’ve registered this type with Unity, thanks to the Common Services Locator provider, we can now access these when importing/injecting into our target types. Let’s create a new MVC Controller to demonstrate this:

[ExportController("Support")]
public class SupportController : Controller
{
    #region Fields
    private ITicketSystem ticketSystem;
    #endregion

    #region Constructor
    [ImportingConstructor]
    public SupportController(ITicketSystem ticketSystem)
    {
        this.ticketSystem = ticketSystem;
    }
    #endregion

    #region Actions
    public ActionResult Index()
    {
        var ticket = ticketSystem.CreateTicket();
        return Content(ticket.Title);
    }
    #endregion
}

With our new controller, when we create an instance, we inject our ITicketSystem instance through the constructor. The important thing here, is that our instance of ITicketSystem is provided by our IoC container (currently Unity), exposed via MEF. Thus demonstrates just how flexible and robust MEF really is.

We can even do it for another IoC container, let’s try Autofac:

public class MvcApplication : Application
{
  #region Fields
  private CSLExportProvider exportProvider;
  #endregion

  #region Methods
  /// <summary>
  /// Creates the instance of the Unity container.
  /// </summary>
  protected override void PreCompose()
  {
      var container = new ContainerBuilder().Build();
      exportProvider = new CSLExportProvider(new AutofacServiceLocator(container));

      RegisterTypes(container);
  }

  /// <summary>
  /// Registers any types required for the container.
  /// </summary>
  /// <param name="container">The container.</param>
  private void RegisterTypes(IContainer container)
  {
      var builder = new ContainerBuilder();

      builder.RegisterType<SimpleTicketSystem>().As<ITicketSystem>();
      exportProvider.RegisterType(typeof(ITicketSystem));

      builder.Update(container);
  }

  /// <summary>
  /// Creates the composer used for composition.
  /// </summary>
  /// <returns></returns>
  protected override MefMvcFramework.Composition.Composer CreateComposer()
  {
      var composer = base.CreateComposer();
      composer.AddExportProvider(exportProvider);

      return composer;
  }
  #endregion
}

Minimal code change makes me very happy! Any CSL compatible provider can be used, allowing enormous flexibility and integration options. Hope you like it!

Download VS2010 Project

UPDATE For Martyn: wiring up Castle Windsor was really easy, in fact took my only a few minutes to wire it up and add a component to the container. This is again thanks to the CSL project, whereby you can grab a CSL-provider for Castle Windsor.

The code change is as follows:

namespace MefMvcApplication
{
    using Castle.Windsor;
    using CommonServiceLocator.WindsorAdapter;

    using MefMvcFramework.Example.TicketSystem;
    using MefMvcFramework.ServiceLocation;
    using MefMvcFramework.Web;

    public class MvcApplication : Application
    {
        #region Fields
        private CSLExportProvider exportProvider;
        #endregion

        #region Methods
        /// <summary>
        /// Creates the instance of the Unity container.
        /// </summary>
        protected override void PreCompose()
        {
            var container = new WindsorContainer();
            exportProvider = new CSLExportProvider(new WindsorServiceLocator(container));

            RegisterTypes(container);
        }

        /// <summary>
        /// Registers any types required for the container.
        /// </summary>
        /// <param name="container">The container.</param>
        private void RegisterTypes(IWindsorContainer container)
        {
            container.AddComponent("TicketSystem", typeof(ITicketSystem), typeof(SimpleTicketSystem));
            exportProvider.RegisterType(typeof(ITicketSystem));
        }

        /// <summary>
        /// Creates the composer used for composition.
        /// </summary>
        /// <returns></returns>
        protected override MefMvcFramework.Composition.Composer CreateComposer()
        {
            var composer = base.CreateComposer();
            composer.AddExportProvider(exportProvider);

            return composer;
        }
        #endregion
    }
}

The Castle Windsor variant of the project is attached. Hope that helps.

Download VS 2010 Project (Castle Windsor)

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)