Getting started with Umbraco 7 and StructureMap v3

,

Lately I was experimenting a little bit with Umbraco 7 and Hybrid Framework by Jeroen Breuer. It’s a great Umbraco extension, definitely worth checking. One thing I missed was some Dependency Injection (DI) container to inject custom data repositories into WebApi Controllers.  After some reaserch I decided to give a chance to StructureMap.

Although code in this example is written based on StructureMap syntax, the general way of inserting IoC/DI containers into Umbraco project should be very similar for other containers like Autofac, Ninject or Unity.

In next steps I will show how to make StructureMap works with Umbraco 7 from very empty project. The solution structure will be similar to Hybrid Framework so you could also use it as starting point.

First things first

At the beginning lets setup an Umbraco project in Visual Studio. To do this you need to create empty web application name Umbraco.Site and add Umrbaco NuGet Package. You can do this by NuGet Manager or via Package Manager Console by executing:

Install-Package UmbracoCms

After this you should have ready-to-install Umbraco instance. If you are experiencing any troubles during installing Umbraco from NuGet please refer to official tutorial.

Next, let’s create another project, this time for holding our custom logic. Let’s name it Umbraco.Extensions and select Class Library project type.

Last thing in this step is to add Reference from Umbraco.Extensions to Umbraco.Site.

Installing StructureMap

Although Umbraco is using MVC 4, in this step we are going to use StructureMap NuGet package for version 5. This package is using newer StructureMap version (v3) and it works perfectly also with MVC 4.

To install StructureMap select Umbraco.Extensions project, go to NuGet Manager and install StructureMap MVC5 package.

If you prefere to use Package Manager Console please execute:

Install-Package StructureMap.MVC5

Just remember to choose correct project in Default Project dropdown inside Package Manager Console window.

Image 2015-02-16 at 10.44.59

After installation couple new items will appear in your Solution Explorer tree and current solution structure should looks like bellow:

Image 2015-02-16 at 10.46.55

To make everything works, in this project we will also need some of Umbraco libraries. You can install them via NuGet Manager, searching for Umbraco Cms Core Binaries or via Package Manager Console, executing:

Install-Package UmbracoCms.Core

To build the solution we only miss System.Web reference in Umbraco.Extensions project. Add it and try if your solution is building correctly at this stage, it will throw some exceptions at runtime but it should build with no errors.

Making things work

Thanks to NuGet packages we have almost everything ready. One thing that we really should care of is prevent StructureMap to override way that Umbraco is handling Controllers. To do this let’s create helper class named ControllersHelper with method IsUmbracoController. I found inspiration for this method in Rene’s post at our.umbraco.org, so credits goes to him 🙂

public static class ControllersHelper
{
   public static bool IsUmbracoController(Type controllerType)
   {
      return controllerType.Namespace != null
         && controllerType.Namespace.StartsWith("umbraco", StringComparison.InvariantCultureIgnoreCase) 
         && !controllerType.Namespace.StartsWith("umbraco.extensions", StringComparison.InvariantCultureIgnoreCase);
   }
}

Basically what is does, it’s checking if controller belongs to Umbraco. Just pay attention that I’m also checking if controller is not from Umbraco.Extensions namespace, because in this example (and in Hybrid Framework) that is the namespace of our custom project, not Umbraco itself.

In next step we will move to StructureMapDependencyScope.cs file. This is file containing methods responsible for resolving dependencies. At first stop let’s go to DoGetInstance method. In this place we need to ensure that if we are handling Umbraco controller, we let Umbraco do the business. Bellow you can find modified version of this method:

protected override object DoGetInstance(Type serviceType, string key)
{
   IContainer container = (CurrentNestedContainer ?? Container);

   if (ControllersHelper.IsUmbracoController(serviceType))
   {
      return Activator.CreateInstance(serviceType) as IHttpController;
   }

   if (string.IsNullOrEmpty(key))
   {
      return serviceType.IsAbstract || serviceType.IsInterface
         ? container.TryGetInstance(serviceType)
         : container.GetInstance(serviceType);
   }

   return container.GetInstance(serviceType, key);
}

As you can see, there is condition where Umbraco controllers are letting to handle by Activator and prevent from handling by StructureMap. If you have some previous experience with DI/IoC and Umbraco, I can tell you that it will allow you to skip declarations such as For<UmbracoContext>.Use(UmbracoContext.Current) or For<ApplicationContext>().Use(ApplicationContext.Current). This is a quite common problem with DI and Umbraco.

In same file there is two more things that needs to be modified. It’s not something what will break the application at runtime if omitted but it will generate some errors during debugging.

So here is the first one:

public IContainer CurrentNestedContainer {
   get {
      if (HttpContext != null)
         return (IContainer)HttpContext.Items[NestedContainerKey];
      return null;
   }
   set {
      HttpContext.Items[NestedContainerKey] = value;
   }
}

And here is second one:

private HttpContextBase HttpContext {
   get {
      var ctx = Container.TryGetInstance<HttpContextBase>();
      return ctx ?? (System.Web.HttpContext.Current != null ? new HttpContextWrapper(System.Web.HttpContext.Current) : null);
   }
}

Handling WebApi calls

We are almost there. This is the last thing to get StructureMap working with Umbraco. Default way suggested by StructureMap authors is to handle WebApi calls by replace DependencyResolver instance. This could work but unfortunately Umbraco uses it’s own DependencyResolver so overriding it will break all WebApi calls, including almost all requests made in Backoffice. It also would break when creating instance of InstallApiController, because it has only protected constructors so it’s not possible to create it from outside of Umbraco. To solve this let’s use IHttpControllerActivator insted. If you would like to read something more about differences between using IDependencyResolver and IHttpControllerActvator please refer to this great post by Mark Seemann. Bellow you can find implementation of UmbracoWebApiHttpControllerActivator. It’s also inspired by Rene’a post I’ve mention before with few modifications.


public class UmbracoWebApiHttpControllerActivator : IHttpControllerActivator
{
   private readonly DefaultHttpControllerActivator _defaultHttpControllerActivator;

   public UmbracoWebApiHttpControllerActivator()
   {
      this._defaultHttpControllerActivator = new DefaultHttpControllerActivator();
   }

   public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
   {
      IHttpController instance =
         ControllersHelper.IsUmbracoController(controllerType)
            ? this._defaultHttpControllerActivator.Create(request, controllerDescriptor, controllerType)
            : StructuremapMvc.StructureMapDependencyScope.GetInstance(controllerType) as IHttpController;

      return instance;
   }
}

So here, when it’s controller from Umbraco namespace we are gonna handle it with DefaultHttpControllerActivator, otherwise we let StructureMap do the work.

Now we only need to set our UmbracoWebApiHttpControllerActivator as the one used during handling WebApi calls. To do this create UmbracoEvents class (or navigate to existing one in Hybrid Framework).


public class UmbracoEvents : ApplicationEventHandler
{
   protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
   {
      GlobalConfiguration.Configuration.Services.
         Replace(typeof(IHttpControllerActivator), new UmbracoWebApiHttpControllerActivator());
   }
}

From this moment everything, including Installation and Updates, should work fine.

Testing

In final step let’s check if it really works. We are gonna use example from Umbraco WebApi documentatnion and create simple ApiController to return list of some products.

public interface IProductRepository
{
   IEnumerable<string> GetAllProducts();
}

public class ProductRepository : IProductRepository
{
   public IEnumerable<string> GetAllProducts()
   {
      return new[] { "Table", "Chair", "Desk", "Computer", "Beer fridge", "Mouse" };
   }
}

public class ProductsController : UmbracoApiController
{
   private IProductRepository _repo;
   public ProductsController(IProductRepository repo)
   {
       _repo = repo;
   }
   public IEnumerable<string> GetAllProducts()
   {
      return _repo.GetAllProducts();
   }
}

Because we are still in the same assembly that init StructureMap, the dependencies will be injected automatically, so there is no need to register any of them by hand.

So, now just navigate to /umbraco/api/products/getallproducts

If you have any more questions pleas leave them in comments bellow, I will be happy to answer all of them 🙂

 


Related articles

  • Good information, thanks. I’ve been using IoC with Umbraco using Ninject, and at least on the face of it, looks to be easier to hook up without quite so much plumbing. There’s a nice write-up here: https://ismailmayat.wordpress.com/2013/07/25/umbraco-6-mvc-and-dependency-injection/

    • Hi Andy, thanks for good word! Main problem with DI/IoC in Umbraco is it’s getting pretty messy during version update. I didn’t try Ninject by myself but I saw people also facing this problem with that container. If you can please let me know if you ever had any troubles with Ninject when updating Umbraco to newer version. Cheers!

      • Can’t say I’ve seen that to be honest, at least for minor/patch upgrades. Not tried it for say late v4 to v7.

        • Lee

          I use Unity with Umbraco 7, but as with Ninject it has problems when you try to upgrade. You get YSOD errors about parameterless constructors. I still haven’t figured it out, so the only way to get around it is to disable Unity on startup when trying to upgrade umbraco.

  • Vishal Dubey

    If we would be in different assembly that init StructureMap, then how do we get ProductsController instance that has dependency IProductRepository.

  • Carl Jackson

    Has anyone tried this with Umbraco 7.3 +. I had this working really well but after upgrading to Umbraco 7.3 it doesn’t work. all Controllers with constructor parameters just throw a “No parameterless constructor defined” error. Any help appreciated.