ASP.NET MVC 4, Web API and StructureMap
Share This
ASP.NET MVC 4 is now released and along with it Web API! This post is not going to cover the new features of MVC 4 or all the good stuff Web API brings to the table, but will cover them in the context of Dependency Resolution. As you may have expected the new ApiController supports Dependency Injection, however you may not have expected that your MVC 3 IDepdencyResolver implementation would not work for instances of ApiController.
If you Install-Package StructureMapMVC3 and create a mapping:
Then try to use constructor injection in a Web API controller:
You will receive the 'Type Controllers.AwesomeApiController does not have a default constructor' argument exception.
What, why?
This is because Web API is a completely different stack than MVC and does not have access to the DependencyResolver from System.Web.Mvc. This was by design so that Web API could be self hosted. Web API's ability to self host is awesome but this means we now have two interfaces named IDependencyResolver.
- System.Web.Http.Dependencies.IDependencyResolver (Web API)
- System.Web.Mvc.IDependencyResolver (MVC)
The IDependencyResolver interface for Web API inherits two other interfaces, IDependencyScope and IDisposable:
The IDependencyResolver only has a single method that returns and instance of IDependencyScope:
The IDependencyScope interface defines two methods:
- GetService : Creates one instance of a specified type.
- GetServices : Create a collection of objects of a specified type.
Web API IDependencyResolver and StructureMap
The dependency resolver attached to the HttpConfiguration object has global scope. When the framework creates a new instance of a controller, it calls IDependencyResolver.BeginScope. This method returns an IDependencyScope. The framework calls GetService on the IDependencyScope instance to get the controller. When the framework is done processing the request, it calls Dispose on the child scope. You can use the Dispose method to dispose of the controller’s dependencies.
We can simply implement the needed interfaces using StructureMap as our container:
The StructureMapDependencyScope class implements IDependencyScope and represents a child scope. The StructureMapDependencyResolver class implements the global-scope dependency resolver. In the BeginScope method, it creates a new StructureMapDependencyScope instance. The StructureMap container also has the concept of a child container, so we initialize the child StructureMapDependencyScope with a StructureMap child/nested container. The StructureMapDependencyScope.Dispose method disposes of the StructureMap child container.
Now we can wire this up in our App_Start:
MVC and CommonServiceLocator
This takes care of Web API but is not much help for MVC. If you look at the System.Web.Mvc.DependencyResolver.SetResolver method, you will notice it has three overloads
- void SetResolver(System.Func getService, System.Func> getServices)
- void SetResolver(System.Web.Mvc.IDependencyResolver resolver)
- void SetResolver(object commonServiceLocator)
In the past we would have used the overload that takes an instance of MVC's IDependencyResolver. Because I would like to use my StructureMapDependencyResolver for both Web API and MVC, we can use the overload that takes an instance of CommonServiceLocator. CommonServiceLocator is an interface for dependency injection that all vendors can implement.
There are a few ways to have the adapter implement the interface. You can either use the IServiceLocator interface or you can use ServiceLocatorImplBase (Microsoft.Practices.ServiceLocation), which is used as a convenience for those who wants to implement the IServiceLocator interface:
With the ServiceLocatorImplBase as our base, we only need to implement the DoGetInstance and DoGetAllInstances methods:
The updated StructureMapDependencyResolver:
The updated App_Start:
The good news!
The good news is, thanks to NuGet, you don't need to worry about any of the pluming needed to get this working. By using
and you are all set. For those who would like to see the implementation details, you can view the code here StructureMap.MVC4.