Earlier this year Mike Wasson has published a post: "Dependency Injection in ASP.NET Web API 2" that describes Web API's approach to the Dependency Injection design pattern.
In short it goes like this:
HttpConfiguration.DependencyResolver
The Unity Container (Unity) is a lightweight, extensible dependency injection container. There are Nugets both for Unity library and for Web API integration.
Now to the point of this post.
Unity defines a hierarchy of injection scopes. In Web API they are usually mapped to application and request scopes. This way a developer can inject application singletons, create request level, or transient objects.
Everything looks reasonable. The only problem we have found is that there is no way you to inject Web API objects like HttpConfiguration, HttpControllerContext or request's CancellationToken, as they are never registered for injection.
HttpConfiguration
HttpControllerContext
CancellationToken
To workaround this we have created a small class called UnityControllerActivator that perfroms required registration:
UnityControllerActivator
using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using Microsoft.Practices.Unity; /// <summary> /// Unity controller activator. /// </summary> public class UnityControllerActivator: IHttpControllerActivator { /// <summary> /// Creates an UnityControllerActivator instance. /// </summary> /// <param name="activator">Base activator.</param> public UnityControllerActivator(IHttpControllerActivator activator) { if (activator == null) { throw new ArgumentException("activator"); } this.activator = activator; } /// <summary> /// Creates a controller wrapper. /// </summary> /// <param name="request">A http request.</param> /// <param name="controllerDescriptor">Controller descriptor.</param> /// <param name="controllerType">Controller type.</param> /// <returns>A controller wrapper.</returns> public IHttpController Create( HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { return new Controller { activator = activator, controllerType = controllerType }; } /// <summary> /// Base controller activator. /// </summary> private readonly IHttpControllerActivator activator; /// <summary> /// A controller wrapper. /// </summary> private class Controller: IHttpController, IDisposable { /// <summary> /// Base controller activator. /// </summary> public IHttpControllerActivator activator; /// <summary> /// Controller type. /// </summary> public Type controllerType; /// <summary> /// A controller instance. /// </summary> public IHttpController controller; /// <summary> /// Disposes controller. /// </summary> public void Dispose() { var disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } } /// <summary> /// Executes an action. /// </summary> /// <param name="controllerContext">Controller context.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>Response message.</returns> public Task<HttpResponseMessage> ExecuteAsync( HttpControllerContext controllerContext, CancellationToken cancellationToken) { if (controller == null) { var request = controllerContext.Request; var container = request.GetDependencyScope(). GetServices(typeof(IUnityContainer)) as IUnityContainer; if (container != null) { container.RegisterInstance<HttpControllerContext>(controllerContext); container.RegisterInstance<HttpRequestMessage>(request); container.RegisterInstance<CancellationToken>(cancellationToken); } controller = activator.Create( request, controllerContext.ControllerDescriptor, controllerType); } controllerContext.Controller = controller; return controller.ExecuteAsync(controllerContext, cancellationToken); } } }
Note on how it works.
IHttpControllerActivator is a controller factory, which Web API uses to create new controller instances using IHttpControllerActivator.Create(). Later controller's IHttpController.ExecuteAsync() is called to run the logic. UnityControllerActivator replaces original controller activator with a wrapper that delays creation (injection) of real controller untill request objects are registered in the scope
IHttpControllerActivator
IHttpControllerActivator.Create()
IHttpController.ExecuteAsync()
To register this class one need to update code in the UnityWebApiActivator.cs (file added with nuget Unity.AspNet.WebApi)
UnityWebApiActivator.cs
Unity.AspNet.WebApi
public static class UnityWebApiActivator { /// <summary>Integrates Unity when the application starts.<summary> public static void Start() { var config = GlobalConfiguration.Configuration; var container = UnityConfig.GetConfiguredContainer(); container.RegisterInstance<HttpConfiguration>(config); container.RegisterInstance<IHttpControllerActivator>( new UnityControllerActivator(config.Services.GetHttpControllerActivator())); config.DependencyResolver = UnityHierarchicalDependencyResolver(container); } ... }
With this addition we have simplified the boring problem with passing of CancellationToken all around the code, as controller (and other classes) just declared a property to inject:
public class MyController: ApiController { [Dependency] public CancellationToken CancellationToken { get; set; } [Dependency] public IModelContext Model { get; set; } public async Task<IEnumerable<Products>> GetProducts(...) { ... } public async Task<IEnumerable<Customer>> GetCustomer(...) { ... } ... } ... public class ModelContext: IModelContext { [Dependency] public CancellationToken CancellationToken { get; set; } ... }
And finally to perform unit tests for controllers with Depenency Injection you can use a code like this:
using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Dependencies; using System.Net.Http; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.WebApi; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyControllerTest { [ClassInitialize] public static void Initialize(TestContext context) { config = new HttpConfiguration(); Register(config); } [ClassCleanup] public static void Cleanup() { config.Dispose(); } [TestMethod] public async Task GetProducts() { var controller = CreateController<MyController>(); //... } public static T CreateController<T>(HttpRequestMessage request = null) where T: ApiController { if (request == null) { request = new HttpRequestMessage(); } request.SetConfiguration(config); var controllerContext = new HttpControllerContext() { Configuration = config, Request = request }; var scope = request.GetDependencyScope(); var container = scope.GetService(typeof(IUnityContainer)) as IUnityContainer; if (container != null) { container.RegisterInstance<HttpControllerContext>(controllerContext); container.RegisterInstance<HttpRequestMessage>(request); container.RegisterInstance<CancellationToken>(CancellationToken.None); } T controller = scope.GetService(typeof(T)) as T; controller.Configuration = config; controller.Request = request; controller.ControllerContext = controllerContext; return controller; } public static void Register(HttpConfiguration config) { config.DependencyResolver = CreateDependencyResolver(config); } public static IDependencyResolver CreateDependencyResolver(HttpConfiguration config) { var container = new UnityContainer(); container.RegisterInstance<HttpConfiguration>(config); // TODO: configure Unity contaiener. return new UnityHierarchicalDependencyResolver(container); } public static HttpConfiguration config; }
P.S. To those who think Dependency Injection is an universal tool, please read the article: Dependency Injection is Evil.