How to create dependency injection for ASP.NET MVC 5?

Creating Dependency Injection with ASP.NET Core is fairly easy. The documentation explains it very well here and this guy has a killer video to explain it.

However, I want to do the same thing with my ASP.NET MVC 5 project. How can handle dependency injection with ASP.MVC 5?

Also, is Dependency injection limited to controllers only or can it work with any class?


Solution 1:

In ASP.Net MVC you can use the .Net Core DI from NuGet rather than one of the third-party alternatives:-

using Microsoft.Extensions.DependencyInjection

For the MVC Start/Configuration class:-

public void Configuration(IAppBuilder app)
{
    // We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
    var services = new ServiceCollection();

    // configure all of the services required for DI
    ConfigureServices(services);

    // Configure authentication
    ConfigureAuth(app);

    // Create a new resolver from our own default implementation
    var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());

    // Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
    //Other services may be added elsewhere through time
    DependencyResolver.SetResolver(resolver);
}

My project uses Identity User and I've replaced the OWIN start-up configuration to follow a service-based approach instead. The default Identity User classes use static factory methods to create instances. I've moved that code into the constructors and relied on DI to provide the appropriate injection. It is still work in progress but here is where I am at:-

public void ConfigureServices(IServiceCollection services)
{               
    //====================================================
    // Create the DB context for the IDENTITY database
    //====================================================
    // Add a database context - this can be instantiated with no parameters
    services.AddTransient(typeof(ApplicationDbContext));

    //====================================================
    // ApplicationUserManager
    //====================================================
    // instantiation requires the following instance of the Identity database
    services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));

    // with the above defined, we can add the user manager class as a type
    services.AddTransient(typeof(ApplicationUserManager));

    //====================================================
    // ApplicationSignInManager
    //====================================================
    // instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
    services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
    services.AddTransient(typeof(ApplicationSignInManager));

    //====================================================
    // ApplicationRoleManager
    //====================================================
    // Maps the rolemanager of identity role to the concrete role manager type
    services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();

    // Maps the role store role to the implemented type
    services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
    services.AddTransient(typeof(ApplicationRoleManager));
    
    //====================================================
    // Add all controllers as services
    //====================================================
    services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
        .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
    .Where(t => typeof(IController).IsAssignableFrom(t)
    || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
}

The Account Controller class has the single constructor:-

[Authorize]
public class AccountController : Controller
{
    private ApplicationSignInManager _signInManager;
    private ApplicationUserManager _userManager;
    private RoleManager<IdentityRole> _roleManager;

    public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
    {
        UserManager = userManager;
        SignInManager = signInManager;
        RoleManager = roleManager;
    }
}

My Default Dependency Resolver:

/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
    /// <summary>
    /// Provides the service that holds the services
    /// </summary>
    protected IServiceProvider serviceProvider;

    /// <summary>
    /// Create the service resolver using the service provided (Direct Injection pattern)
    /// </summary>
    /// <param name="serviceProvider"></param>
    public DefaultDependencyResolver(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    /// <summary>
    /// Get a service by type - assume you get the first one encountered
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public object GetService(Type serviceType)
    {
        return this.serviceProvider.GetService(serviceType);
    }

    /// <summary>
    /// Get all services of a type
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.serviceProvider.GetServices(serviceType);
    }
}

Solution 2:

For this answer I downloaded a Microsoft Example of WebApi project as a basis for the example and added DI services to it as follows,

  • Update the Target Framework to 4.6.1
  • NuGet the DI package :- Microsoft.Extensions.DependencyInjection

After the standard MapHttpRoute configuration, add code to register which services you need

using's

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;

WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // create the DI services and make the default resolver
        var services = new ServiceCollection();
        services.AddTransient(typeof(DefaultProduct));
        services.AddTransient(typeof(ProductsController));

        var resolver = new MyDependencyResolver(services.BuildServiceProvider());
        config.DependencyResolver = resolver;
    }
}

DefaultProduct

public class DefaultProduct : ProductsApp.Models.Product
{
    public DefaultProduct()
    {
        this.Category = "Computing";
        this.Id = 999;
        this.Name = "Direct Injection";
        this.Price = 99.99M;
    }
}

MyDependencyResolver

/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods.
/// This is combined dependency resolver for MVC and WebAPI usage.
/// </summary>
public class MyDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver 
{
    protected IServiceProvider serviceProvider;
    protected IServiceScope scope = null;

    public MyDependencyResolver(IServiceProvider serviceProvider) 
    {
        this.serviceProvider = serviceProvider;
    }

    public MyDependencyResolver(IServiceScope scope) 
    {
        this.scope = scope;
        this.serviceProvider = scope.ServiceProvider;
    }

    public IDependencyScope BeginScope() 
    {
        return new MyDependencyResolver(serviceProvider.CreateScope());
    }

    public void Dispose() 
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing) 
    {
        scope?.Dispose();
    }

    public object GetService(Type serviceType) 
    {
        return this.serviceProvider.GetService(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType) 
    {
        return this.serviceProvider.GetServices(serviceType);
    }
}

ServiceProviderExtensions

public static class ServiceProviderExtensions
{
    public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
    {
        foreach (var type in serviceTypes)
        {
            services.AddTransient(type);
        }

        return services;
    }
}

I then amended the existing controller to take the DI type (note there is just the one ctor)

using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        DefaultProduct _dp = null;

        public ProductsController(DefaultProduct dp)
        {
            _dp = dp;
            //
            products.Add(dp);
        }

        List<Product> products = new List<Product>()
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };

        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
}

Solution 3:

My Default Dependency Resolver

/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
    /// <summary>
    /// Provides the service that holds the services
    /// </summary>
    protected IServiceProvider serviceProvider;

    /// <summary>
    /// Create the service resolver using the service provided (Direct Injection pattern)
    /// </summary>
    /// <param name="serviceProvider"></param>
    public DefaultDependencyResolver(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    /// <summary>
    /// Get a service by type - assume you get the first one encountered
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public object GetService(Type serviceType)
    {
        return this.serviceProvider.GetService(serviceType);
    }

    /// <summary>
    /// Get all services of a type
    /// </summary>
    /// <param name="serviceType"></param>
    /// <returns></returns>
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.serviceProvider.GetServices(serviceType);
    }
}

Solution 4:

I recommend you use Autofac, there are anothers fwk like unity, ninject, the benchmarks autofac has excelent perfomance.

http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison

Here is the integration with MVC (and works with all class)

http://docs.autofac.org/en/latest/integration/mvc.html

Solution 5:

The simplest way to implements Dependency Injection in ASP.NET MVC 5 is to use the tool developed by Microsoft itself, called Unity.

You can find many resources on the internet about it, and you can start by reading the official documentation available here: Developer's Guide to Dependency Injection Using Unity

Also, is Dependency injection limited to controllers only or can it work with any class?

It works with any class, in any project, as long as you register the Interface related to the Implementation (if you want to take profit of the IoC pattern), all you have to do then is to add the Interface instantiation in your constructor.