Setup StructureMap with ASP.NET Web API Release Candidate

One important change from the Beta of Web API to RC was the way it handles IoC. So far, there have been a few guides of how to do it with Ninject or Unity, but I'm still using StructureMap for many of my projects. In a previous post, I showed how I used StructureMap in an integration test of Web API using NUnit. However, that was against the Beta and was in the context of automated testing. This post works against the RC and is direct usage of StructureMap within the Web API.

Before IoC

Before we start using the IoC, lets look at the sample I created. I have a Web API controller called ValuesController with the GET verb functionality implemented. We'll start with an empty constructor and a controller that looks like this:

[code language="csharp"] public class ValuesController : ApiController { public ValuesController() {}

    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }


    public string Get(int id)
    {
        return "value";
    }
}

[/code]

That's pretty simple. I'm going to put a little jQuery code to call our Web API and confirm it's working without IoC:

[code language="javascript"] $(function () {

 $.getJSON('/api/values/1?timestamp=' + new Date().getTime(), function(data) {
        alert(data);
    });

});

[/code]

After running this, we get this result:

(The timestamp is in there to make sure nothing is caching for demonstration purposes.) Great, a simple Web API controller.

Let's Get Some IoC In There!

However, let's say we now want to add IoC to the controller. In my demo, I started with a new MVC 4 Web API project and pulled down StructureMap from NuGet. It installs itself with 3 files (I realize that I installed the MVC3 version even though there is an MVC4 version available...oh well):

  1. DependencyResolution/IoC.cs
  2. DependencyResolution/SmDependencyResolver.cs
  3. App_Start/StructureMap.cs

Of these three, number 1 won't change at all. In my case, I've got a sample interface called ISampleFish and an implementation class called SampleFish. They are both empty, so don't worry about what's inside them.

Let's now change the constructor on the ValuesController to take in an ISampleFish. Here's what I want:

[code language="csharp"] public class ValuesController : ApiController
{ private readonly ISampleFish _sampleFish;

    public ValuesController(ISampleFish sampleFish)
    {
        _sampleFish = sampleFish;
    }

    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    public string Get(int id)
    {
        return "value";
    }
}

[/code]

Note that I don't actually use the interface in the controller. It's merely a vessel for demonstrating the IoC. Running the same JavaScript again, we now fail. Hitting F12 to open the developer tools, we see this in the network panel:

Digging into the exception information, we see this:

{"ExceptionType":"System.ArgumentException","Message":"Type 'WebApiStructureMapDemo.Controllers.ValuesController' does not have a default constructor","StackTrace":" at System.Linq.Expressions.Expression.New(Type type)rn at System.Web.Http.Internal.TypeActivator.CreateTBasern at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"}

The key part here is: ValuesController does not have a default constructor

This means that the IoC engine isn't being used correctly and the ASP.NET engine cannot find a constructor without any parameters. It's time to fix the IoC.

The StructureMap Doctor Is In

Here's what SmDependencyResolver.cs currently looks like:

[code language="csharp"] public class SmDependencyResolver : IDependencyResolver {

private readonly IContainer _container;

public SmDependencyResolver(IContainer container) {
    _container = container;
}

public object GetService(Type serviceType) {
    if (serviceType == null) return null;
    try {
          return serviceType.IsAbstract || serviceType.IsInterface
                   ? _container.TryGetInstance(serviceType)
                   : _container.GetInstance(serviceType);
    }
    catch {

        return null;
    }
}

public IEnumerable<object> GetServices(Type serviceType) {
    return _container.GetAllInstances(serviceType).Cast<object>();
}

} [/code]

It implements IDependencyResolver, but in this case, the interface is System.Web.Mvc.IDependencyResolver. For Web API, we need a completely different IDependencyResolver. The one we want is System.Web.Http.Services.IDependencyResolver. If we take a look at that interface, it looks like this:

[code language="csharp"] namespace System.Web.Http.Dependencies
{ public interface IDependencyResolver : IDependencyScope, IDisposable { IDependencyScope BeginScope(); } }[/code]

So IDependencyResolver implements IDependencyScope and IDisposable. IDisposable is fairly straight forward. Taking a closer look at IDependencyScope, it looks like this:

[code language="csharp"] namespace System.Web.Http.Dependencies
{ public interface IDependencyScope : IDisposable { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); } }[/code]

So, there's some extra stuff now that we need to take care of. To use StructureMap, we're going to create a StructureMapScope that implements IDependencyScope. We're essentially pulling the code from the original SmDependencyResolver and putting it in the StructureMapScope. It looks like this:

[code language="csharp"] public class StructureMapScope : IDependencyScope
{ protected IContainer Container;

    public StructureMapScope(IContainer container)
    {
        Container = container;
    }

    public void Dispose()
    {
         IDisposable disposable = (IDisposable)Container;
        if (disposable != null)
        {
            disposable.Dispose();
        }
        Container = null;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType == null)
        {
            return null; 
        }
        try
        {
            if (serviceType.IsAbstract || serviceType.IsInterface)
                return Container.TryGetInstance(serviceType);

            return Container.GetInstance(serviceType);
        }
        catch
        {
            return null;
        }
    }

    public IEnumerable&lt;object&gt; GetServices(Type serviceType)
    {
        return Container.GetAllInstances&lt;object&gt;().Where(s =&gt; s.GetType() == serviceType);
    }
}[/code]

This now looks like the original SmDependencyResolver, with the addition being the Dispose() method. So, since StructureMapScope is handling all of this lifting, we need to go back and update SmDependencyResolver. It now looks like this:

[code language="csharp"] public class SmDependencyResolver : StructureMapScope, IDependencyResolver
{ private IContainer _container;

    public SmDependencyResolver(IContainer container)
        : base(container)
    {
        _container = container;
    }

    public IDependencyScope BeginScope()
    {
        _container = (IContainer)IoC.Initialize();
        return new StructureMapScope(_container);
    }
}[/code]

Note that it essentially gets the scope going and StructureMapScope does the main work. The IoC is now done on a per request basis where before it was all globally scoped. We could do the same global scope here and just not do the disposal or reinit in the begin scope (and indeed, I don't know enough about StructureMap to say for sure if IoC.Initialize() is the best thing to do per request). We need to change one more thing in #3 from above: StructureMap.cs. The way the IoC is activated has changed. StructureMap.cs should now look like this:

[code language="csharp"] public static class StructuremapMvc {
public static void Start() { var container = (IContainer)IoC.Initialize(); GlobalConfiguration.Configuration.DependencyResolver = new SmDependencyResolver(container);

}

}[/code]

Once this is all in place, we're back to the alert box from before and all is right with the world. I've put all this together in a GitHub repository.

Let me know what you think of this post in the comments. If you have any suggestions, please let me know. I'm all ears.