unit-testing.png

Unity Dependency Injection in ASP.NET MVC VNext

With out a doubt, Microsoft is having an amazing year. Windows 10 has just been released, the majority of their enterprise development tools have made their way to Github, we’ve got a new Visual Studio including the rebuilt from the ground up Roslyn compiler and we’ll soon be getting a production ready ASP.NET 5 totally rebuilt on top of the new cross platform .NET core. Beta 6 has just been released and I’ve been experimenting with the new Web Application project setup and MVC 6; I have to say Microsoft has truly embraced a modern, community driven approach to web development. However this post isn’t about front end development, nor is it meant to be a forum for my fanboyism. Lets get to the meat and start discussing the new dependency injection framework baked into the new ASP.NET platform.

ASP 5 begins in the Startup class, in the same way as was used in its OWIN predecessor.

public class Startup
{
    // This method gets called by a runtime. Use this method to add services to the container
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddTransient<ITransientService, TransientService>();
        services.AddScoped<IScopedService, ScopedService>();
        services.AddSingleton<ISingletonService, SingletonService>();
    }

    // Configure is called after ConfigureServices is called.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // Configure the HTTP request pipeline.
        app.UseStaticFiles();

        // Add MVC to the request pipeline.
        app.UseMvc();
    }
}

Here you can see services are registered in the ConfigureServices method, afterwards a Configure method is called that adds initialization of external middle-ware. In our case we’re just using MVC. We are given 3 methods to register services based on scope: transient for per resolution instances, scoped for per lifetime instances or singleton global instances.  The built in DI system, in its current version, does have some limitations:

  1. Only constructor injection is supported
  2. Resolved types can only have one constructor
  3. Missing advanced features from existing Inversion of Control containers.

Since this is the case, lets use an existing container.  Autofac has the only working out of the box DI solution for ASP currently.  The Autofac.Framework.DependencyInjection package is available through Nuget.org. But… Personally I think the Unity IoC container is the best DI framework out there and after much trial and error I have finally have a solution. So, lets get this thing working with Unity!

First things first, ASP’s new DI framework uses a few conventions that are incompatible to Unity’s.
The first one we need to take care of is how collections of registrations are handled. This is important since there are sevaral cases where ASP attempts to resolve a collection of a given type as part of a plugin oriented architecture. When performing injection or simple resolves Unity will return an array. In this case the ASP libraries will be expecting an IEnumerable instead. In order to work around this we will need to extend Unity with a resolution strategy for resolving IEnumerable. Fortunately NancyFx already has an implementation for this in their Unity Boostrapper. Grab the source for EnumerableExtension.cs and EnumerableResolutionStrategy.cs at their Nancy.Boostrappers.Unity github repo.

Next we need to wire in Unity to ASP’s service provider. Take a look at the following ConfigureServices method as part of Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var container = new UnityContainer();
    container.Populate(services);

    return container.Resolve<IServiceProvider>();
}

We’ve altered the ConfigureServices method to return an IServiceProvider. ASP will use this ServiceProvider in place of the default one on startup. To get this ServiceProvider we start up a UnityContainer and bootstrap it with the Populate(services) extension and then resolve the provider from the bootstrapped container. To get this to work, we’re going to need a few things. The following was created by using the Autofac.Framework.DependencyInjection ASP 5 implementation as a template.

public class UnityServiceScope : IServiceScope
{
    private readonly IUnityContainer _container;
    public IServiceProvider ServiceProvider { get; }

    public UnityServiceScope(IUnityContainer container)
    {
        _container = container;
        ServiceProvider = _container.Resolve<IServiceProvider>();
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

public class UnityServiceScopeFactory : IServiceScopeFactory
{
    private readonly IUnityContainer _container;

    public UnityServiceScopeFactory(IUnityContainer container)
    {
        _container = container;
    }

    public IServiceScope CreateScope()
    {
        return new UnityServiceScope(CreateChildContainer());
    }

    private IUnityContainer CreateChildContainer()
    {
        var child = _container.CreateChildContainer();
        child.AddExtension(new EnumerableResolutionExtension());

        return child;
    }
}

public class UnityServiceProvider : IServiceProvider
{
    private readonly IUnityContainer _container;

    public UnityServiceProvider(IUnityContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch ()
        {
            return null;
        }
    }
}

Lets look over the components we’ve just defined. These classes provide our middleware implementations for the basic plumbing in ASP 5’s DI framework. IServiceScope is basically a holder for our current scoped container. IServiceScopeFactory is responsible for creating these child scopes from the container it was resolved from. Autofac does this by calling to BeginLifetimeScope, in Unity it’s as simple as calling CreateChildContainer. The third class here, IServiceProvider, is whats actually responsible for resolving registered types. Our implementation is basically just a wrapper for Unity’s Resolve method. One thing to note here is that the DI system in ASP 5 supports optional dependencies. It expects that some resolved dependencies aren’t registered and that a null will be returned instead. To put these in place we define a Populate class extension that registers our implementations in the application container which then resolves our UnityServiceProvider returned from ConfigureServices for ASP to use. But before our provider is ready we need to register all existing services that were added previously by the framework or other middleware, in our case MVC.

public static class UnityRegistration
{
    public static void Populate(this IUnityContainer container,
        IServiceCollection services)
    {
        container.AddExtension(new EnumerableResolutionExtension());

        container.RegisterInstance(services);
        container.RegisterType<IServiceProvider, UnityServiceProvider>();
        container.RegisterType<IServiceScopeFactory, UnityServiceScopeFactory>();

        foreach (var descriptor in services)
        {
            Register(container, descriptor);
        }
    }

    private static void Register(IUnityContainer container,
        ServiceDescriptor descriptor)
    {
        if (descriptor.ImplementationType != null)
        {
            container.RegisterType(descriptor.ServiceType,
                descriptor.ImplementationType,
                GetLifetimeManager(descriptor.Lifetime));

            container.RegisterType(descriptor.ServiceType,
                descriptor.ImplementationType,
                descriptor.ImplementationType.ToString(),
                GetLifetimeManager(descriptor.Lifetime));
        }
        else if (descriptor.ImplementationFactory != null)
        {
            container.RegisterType(descriptor.ServiceType,
                GetLifetimeManager(descriptor.Lifetime),
                new InjectionFactory(unity =>
                {
                    var provider = unity.Resolve<IServiceProvider>();
                    return descriptor.ImplementationFactory(provider);
                }));

            container.RegisterType(descriptor.ServiceType,
                Guid.NewGuid().ToString(),
                GetLifetimeManager(descriptor.Lifetime),
                new InjectionFactory(unity =>
                {
                    var provider = unity.Resolve<IServiceProvider>();
                    return descriptor.ImplementationFactory(provider);
                }));
        }
        else if (descriptor.ImplementationInstance != null)
        {
            container.RegisterInstance(descriptor.ServiceType,
                descriptor.ImplementationInstance,
                GetLifetimeManager(descriptor.Lifetime));

            container.RegisterInstance(descriptor.ServiceType,
                Guid.NewGuid().ToString(),
                descriptor.ImplementationInstance,
                GetLifetimeManager(descriptor.Lifetime));
        }
    }

    private static LifetimeManager GetLifetimeManager(ServiceLifetime lifecycle)
    {
        switch (lifecycle)
        {
            case ServiceLifetime.Transient:
                return new TransientLifetimeManager();
            case ServiceLifetime.Singleton:
                return new ContainerControlledLifetimeManager();
            case ServiceLifetime.Scoped:
                return new HierarchicalLifetimeManager();
        }

        return new TransientLifetimeManager();
    }
}

The register method here accepts each of the existing services and registers them with the unity container. It handles types, factory delegates and existings instances as they were defined in the IServiceCollection descriptors. As each are registered, we add a lifetime as converted by GetLifetimeManager from ASP’s base DI lifetimes: Transient, Singleton or Scoped Singleton. You’ll also notice I’m registering each one twice, once unnamed and again with a name based on the type name. This is so that we can handle both default and collection based type resolutions.

Whew… Now we’re 99% there. This current implementation works for vanilla ASP 5 but as soon as we try to add MVC we start getting object null errors. If you follow the exception into the MVC source code you can see that the service provider is resolving a ScopedInstance<ActionContext> however the reference to ActionContext within that ScopedInstance generic is null. After endless digging though the DI and MVC source code, and dumbfounded trial and error I noticed that if I resolve IScopedInstance<ActionContext> in the Startup ConfigureServices method it suddenly starts working.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var container = new UnityContainer();
    container.Populate(services);
    container.Resolve<IScopedInstance<ActionContext>>();

    return container.Resolve<IServiceProvider>();
}

I have one theory as to why this works. When Unity encounters a request to resolve a concrete type that has no registration it will automatically attempt to create a new instance and return it. For other containers such as Autofac, this is not the case. Instead they will throw an exception that the concrete type hasn’t been registered. I believe MVC is using this behavior to determine if a new ActionContext needs to be instantiated in the root scope before creating a new one. By allowing the root container to create a new instance of ActionContext we’re performing this action before MVC fails at it.

I have scoured MVC’s source code looking for where this might be happening but have yet to come up with an answer. If you have any insight on this feel free to comment or contact me directly. I’m looking forward to see how the Microsoft Patterns and Practices team approaches this in the future as well as what changes we might see by the ASP 5 RTM due out this November.

UPDATE: This issue has been resolved as of beta 7. IScopedInstance has been removed entirely

Leave a comment below!

comments

Stacy GayUnity Dependency Injection in ASP.NET MVC VNext
Share this post