CHAPTER 6
A number of technologies in the Microsoft stack use IoC. In fact, its number seems to be growing every time a new version or technology is launched. It is generally possible to plug in an IoC container of choice; in our case, let’s see how to use Unity.
The ASP.NET Web Forms framework doesn’t allow the retrieval of pages or controls from an IoC container. It does, however, allow injecting dependencies on a page after it was created. One way to do that is by rolling our own implementation of IHttpHandlerFactory, the interface that is responsible for creating handlers (such as pages), and adding the dependency injection behavior.
Our page handler factory could be:
public class UnityHandlerFactory : PageHandlerFactory { public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path) { var handler = base.GetHandler(context, requestType, virtualPath, path); var unity = ServiceLocator.Current.GetInstance<IUnityContainer>(); unity.BuildUp(handler.GetType(), handler); return (handler); } } |
We inherit from PageHandlerFactory because it knows better than we do about how to build pages. After it does that, we call BuildUp to inject any required dependencies. We need to register the page handler factory on the Web.config file as the default factory for all .aspx requests; it should go in the handlers section of system.webServer:
<system.webServer> <handlers> <add path="*.aspx" type="Succinctly.UnityHandlerFactory, Succinctly" verb="*" name="UnityHandlerFactory"/> </handlers> </system.webServer> |
For injecting dependencies in controls, we should call BuildUp explicitly upon them, since there is no hook that allows us to do that automatically.
When we have components that use the PerRequestLifetimeManager, these components don’t get disposed of automatically at the end of the request unless we enable the module UnityPerRequestHttpModule. This module is part of the Unity bootstrapper for ASP.NET MVC package but don’t mind the name: it can be useful for Web Forms projects as well. What it does is, at the EndRequest event, it checks all of the components that were created by Unity and implements IDisposable and calls Dispose upon them. Register UnityPerRequestHttpModule in the modules section of system.webServer:
<system.webServer> <modules> <add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc"/> </modules> </system.webServer> |
Tip: An alternative approach to using UnityPerRequestHttpModule is to create a child container and store it in HttpContext.Items and dispose of it in EndRequest.
ASP.NET MVC’s dependency resolution interface is IDependencyResolver:
public interface IDependencyResolver { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); } |
The methods it exposes should be very familiar to us; the only differences to Unity/Common Service Locator is that they don’t have strong-typed versions and they don’t accept named registrations. A simple implementation is:
public class UnityDependencyResolver : IDependencyResolver { private readonly IUnityContainer unity;
public UnityDependencyResolver(IUnityContainer unity) { this.unity = unity; }
public object GetService(Type serviceType) { try { var svc = this.unity.Resolve(serviceType); return (svc); } catch { return (null); } }
public IEnumerable<object> GetServices(Type serviceType) { try { var svcs = this.unity.ResolveAll(serviceType); return (svcs); } catch { return (Enumerable.Empty<object>()); } } } |
Tip: Note that, unlike Unity, the IDependencyResolver resolution methods cannot throw.
Then, when we register a controller factory, we implement our own by inheriting from DefaultControllerFactory and we pass it our Unity instance:
public class UnityControllerFactory : DefaultControllerFactory { private readonly IUnityContainer unity;
public UnityControllerFactory(IUnityContainer unity) { this.unity = unity; }
public override IController CreateController(RequestContext requestContext, string controllerName) { var controller = unity.Resolve<IController>(controllerName);
if (controller == null) { controller = base.CreateController(requestContext, controllerName); } if (controller != null) { unity.BuildUp(controller.GetType(), controller); }
return (controller); } } |
This controller factory first tries to resolve a registered controller from Unity and then, if one is not found, falls back to the default creation method. If a controller is found, it tries to inject all of its dependencies.
We should register our dependency resolver in the Global.asax code-behind class; for example, in Application_Start:
DependencyResolver.SetResolver(new UnityDependencyResolver(unity)); ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(unity)); |
Of course, for it to find a controller, you have to register one. You can do that explicitly or by using a convention (see Registration by Convention). An explicit registration is as follows:
unity.RegisterInstance<IController>("Home", new HomeController()); |
Besides returning the full controller instance, we can inject parameters in action methods automatically. First, let’s start by defining a custom model binder (IModelBinder implementation):
public sealed class ServiceLocatorModelBinder : IModelBinder { public Object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { return (ServiceLocator.Current.GetInstance(bindingContext.ModelType)); } } |
We need to set our model binder on the parameter we wish to inject. We do this by applying a ModelBinderAttribute; no additional registration is required:
public ActionResult Index( [ModelBinder(typeof(ServiceLocatorModelBinder))] ILogger logger) { return this.View(); } |
To dispose of registered instances, you should also register the UnityPerRequestHttpModule module. See this discussion in For injecting dependencies in controls, we should call BuildUp explicitly upon them, since there is no hook that allows us to do that automatically.
Disposing of Components.
ASP.NET Web API also has an extensible dependency resolution interface named IDependencyResolver but with a slightly more complex interface (which we can see flattened here):
public interface IDependencyResolver : IDependencyScope, IDisposable { object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType); IDependencyScope BeginScope(); void Dispose(); } |
I suggest the following implementation:
public class UnityDependencyResolver : IDependencyResolver { private readonly IUnityContainer unity; private readonly IDependencyResolver baseResolver; private readonly LinkedList<IUnityContainer> children = new LinkedList<IUnityContainer>(); public UnityDependencyResolver(IUnityContainer unity, IDependencyResolver baseResolver) { this.unity = unity; this.baseResolver = baseResolver; }
public IDependencyScope BeginScope() { var child = this.unity.CreateChildContainer(); this.children.AddLast(child); return (new UnityDependencyResolver(child)); }
public object GetService(Type serviceType) { try { var svc = this.unity.Resolve(serviceType); return (svc); } catch { return (null); } }
public IEnumerable<object> GetServices(Type serviceType) { try { var svcs = this.unity.ResolveAll(serviceType); return (svcs); } catch { return (Enumerable.Empty<object>()); } }
public void Dispose() { foreach (var child in this.children) { child.Dispose(); } this.children.Clear(); } } |
Tip: Tip: Again note that, unlike Unity, the IDependencyResolver resolution methods cannot throw.
Three things to keep in mind:
For dependency injection in controllers, we should also implement our own IHttpControllerActivator, which is the interface responsible for creating controller instances. This ensures that, even if it’s not Unity that creates an instance of a controller, its dependencies are still injected properly. An example could be:
public class UnityControllerActivator : IHttpControllerActivator { private readonly IUnityContainer unity; private readonly IHttpControllerActivator baseActivator;
public UnityControllerActivator(IUnityContainer unity, IHttpControllerActivator baseActivator) { this.unity = unity; this.baseActivator = baseActivator; }
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { var controller = GlobalConfiguration.Configuration.DependencyResolver .GetService(controllerType) as IHttpController;
if (controller == null) { controller = this.baseActivator.Create(request, controllerDescriptor, controllerType);
if (controller != null) { this.unity.BuildUp(controller.GetType(), controller); } } return (controller); } } |
All registration goes in Global.asax.cs, method Application_Start:
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver( unity); GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new UnityControllerActivator(unity, GlobalConfiguration.Configuration.Services .GetHttpControllerActivator())); |
For action methods, it is equally as easy to inject resolved values. Let’s define a DependencyResolverParameterBindingAttribute by inheriting from ParameterBindingAttribute:
[Serializable] [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] public sealed class DependencyResolverParameterBindingAttribute : ParameterBindingAttribute { public override HttpParameterBinding GetBinding( HttpParameterDescriptor parameter) { return (new DependencyResolverParameterBinding(parameter)); } } |
And an associated parameter binder class (HttpParameterBinding):
public sealed class DependencyResolverParameterBinding : HttpParameterBinding { public DependencyResolverParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor) { } public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { if (actionContext.ControllerContext.Configuration.DependencyResolver != null) { actionContext.ActionArguments[this.Descriptor.ParameterName] = actionContext.ControllerContext.Configuration.DependencyResolver.GetService (this.Descriptor.ParameterType); } return(Task.FromResult(0)); } } |
With these, it is easy to say that an action method’s parameter should come from Unity:
[HttpGet] [Route("api/home/index")] public IQueryable<string> Index( [DependencyResolverParameterBinding] ILogger logger) { return (new [] { "a", "b", "c" }.AsQueryable()); } |
The logger parameter is automatically injected by MVC.
Entity Framework 6 brought along extensibility in the form of a dependency resolution API. It defines an interface for resolving components but only contains simple implementations of it (ExecutionStrategyResolver<T>, SingletonDependencyResolver<T>, and TransactionHandlerResolver). Instead, Entity Framework allows developers to plug in their own IoC of choice by implementing a simple adapter.
The dependency resolution interface is IDbDependencyResolver and it is a very simple one, only two methods:
public interface IDbDependencyResolver { object GetService(Type type, object key); IEnumerable<object> GetServices(Type type, object key); } |
As you can see, these methods match closely to Unity’s own component resolution ones. Therefore, a Unity adapter for Entity Framework’s dependency resolution interface could be:
public class UnityDbDependencyResolver : IDbDependencyResolver { private readonly IUnityContainer unity;
public UnityDbDependencyResolver(IUnityContainer unity) { this.unity = unity; }
public object GetService(Type type, object key) { try { var component = this.unity.Resolve(type, (key ?? "").ToString()); return (component); } catch { return (null); } }
public IEnumerable<object> GetServices(Type type, object key) { try { var components = this.unity.ResolveAll(type); return (components); } catch { return (Enumerable.Empty<object>()); } } } |
Tip: Again, the IDbDependencyResolver resolution method cannot throw.
Like I said, Entity Framework already contains a private implementation of this interface and it supports a dependency resolution pipeline. You register your adapter in a DbConfiguration class with the same prefix as your DbContext so that Entity Framework can find it automatically, or you add a DbConfigurationTypeAttribute to it by calling AddDefaultResolver. This ensures that your resolver will be the first to be used:
public class MyContextConfiguration : DbConfiguration { public MyContextConfiguration() { this.AddDefaultResolver(new UnityDbDependencyResolver(unity)); } } |