68

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?

Jaylen
  • 39,043
  • 40
  • 128
  • 221
  • 2
    There are many 3rd DI Containers out there, and they all have their own excellent documentation about how to plug-in their container into an MVC app. For instance [Simple Injector](https://simpleinjector.readthedocs.io/en/latest/mvcintegration.html), [Autofac](https://autofac.readthedocs.io/en/latest/integration/mvc.html) or [Ninject](https://github.com/ninject/ninject.web.mvc). – Steven Apr 09 '17 at 19:52
  • 1
    For me I would prefer to go with an example project which explains in details and step by step how to use dependency injection for ASP.NET MVC5.. I found it in a great book called Pro ASP.NET MVC 5 .. chapter 6. interesting book you should read it! – Ayman Oct 07 '17 at 20:16
  • Chapter 3 for me. Pro ASP.NET MVC 5, 5th edition by Freeman. – xr280xr May 28 '20 at 17:30

9 Answers9

53

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);
    }
}
jrummell
  • 42,637
  • 17
  • 112
  • 171
  • I just tried this but installation failed saying that it did not support the framework I was targetting (4.5.1). I was very sad :( – BVernon Jan 13 '18 at 21:52
  • 2
    @BVernon If you can upgrade to 4.6.1, I think it will let you install it. – joshmcode Apr 21 '18 at 18:39
  • 4
    Shouldn't your AccountController private variables be interfaces, not concrete classes? – joshmcode Apr 21 '18 at 20:51
  • 1
    @joshmcode Yeah unfortunately that's the problem for me; it's not a good time for us to do that. Eventually we will but I ended up just getting Castle.Windsor as I found some code snippets that handled the two issues I was concerned with: integrating the DI container with the controllers and also with Solr search. – BVernon Apr 23 '18 at 04:55
  • @pixelda from what namespace do you get "DefaultDependencyResolver"-Class? – Darem Jul 09 '18 at 12:46
  • @ joshmcode - quite right, but not a high priority at the moment. –  Jul 10 '18 at 13:22
  • @Darem, I don't see a built-in DefaultDependencyResolver. Looks like you have to write it yourself? Here's a sample implementation: https://gist.github.com/andriybuday/a96bfc7845ba1514561a4e6f146a629d#file-2-defaultdependencyresolver-cs – Matt Varblow May 24 '19 at 20:22
  • Hello, I am using .NET 4.7.2 with Identity. Is it true that if I was using .NET core 2+ DI would be built in with the use of the app.CreatePerOwinContext lines within the StartUp's ConfigureAuth function? or does it still require this answer for DI to work correctly? Very confused at this point because the VS template output code has it all set up as if DI works out of the box. However, I guess that is the reason for the "return _signInManager ?? HttpContext.GetOwinContext().Get();" in the getter of ApplicationSignInManager. If not set(.Net 4.X) get it – Eddie Jul 23 '19 at 16:26
  • didn't the OP specifically say ASP.NET MVC 5, and specifically exclude MVC core? It seems that this code works with .NET core only – stephenbayer Aug 08 '19 at 00:14
  • I tried editing the resolver into this answer, since it really should be part of this answer, and it's silly to expect someone reaching this page to review other answers farther down and synthesize them into a working solution. Unfortunately, mods rejected my edit as a drastic change. pixelda, would you mind making that edit yourself? – Christopher Berman Oct 04 '19 at 14:38
  • im trying to make AddTransient => AddScoped on my ApplicationDbContext but it keeps tuning me https://stackoverflow.com/questions/60101329/di-scoped-dbcontext-ef6-a-second-operation-started-on-this-context-before-a, should your example not be scoped for service and repos – Seabizkit Feb 07 '20 at 10:42
  • Hello, in my case compiler shows an error at this instruction: `services.AddTransient, ApplicationRoleManager>();`. It says: `Type 'ApplicationRoleManager' cannot be used as type parameter 'TImplementation' in generic type or method 'ServiceCollectionServiceExtensions.AddTransient(IServiceCollection)'`. I don't know exact message since I am translating from Spanish. What may be causing this? – jstuardo Apr 29 '20 at 23:42
  • @Seabizkit and anyone else: the linked question is withdrawn so i cannot see what it was, but i guess i wanted to also know if this example should be enough to actually generate Scoped service instances (tied to request lifecycle). when i AddScoped and later retrieve the service, it always comes back the same across multiple requests. possibly need to invoke ControllerBuilder.Current.SetControllerFactory... per https://stackoverflow.com/questions/53993727/asp-net-dependency-injection-scoped-life-time – nicholas Sep 10 '21 at 01:16
39

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);
        }
    }
}
Stanislav Berkov
  • 5,929
  • 2
  • 30
  • 36
  • 5
    Be careful with `public IDependencyScope BeginScope() { return this; }`. If you do this you will get memory leak. You need to call `_serviceProvider.CreateScope()`, and new constructor to dependency resolver that accepts it and return new resolver instance. Then in `Dispose` dispose the scope. – Stanislav Berkov Sep 17 '20 at 06:29
  • 1
    Thanks @StanislavBerkov!! You're right! I've updated the answer. How did you know that?? – spottedmahn Dec 14 '20 at 01:05
  • 3
    @spottedmahn I get memory leak in prod and started investigating why) – Stanislav Berkov Dec 14 '20 at 08:18
  • 1
    @StanislavBerkov after re-reading your comment a few more times I still don’t think we have the correct implementation in this answer. Thoughts? – spottedmahn Dec 14 '20 at 08:31
  • 1
    Thanks @StanislavBerkov!! Your implementation makes more sense than mine . Why the virtual dispose though? – spottedmahn Dec 15 '20 at 13:17
  • I've follows the instructions here but get the error message: `InvalidOperationException: An error occurred when trying to create a controller of type 'MyApp.Controllers.HomeController'. Make sure that the controller has a parameterless public constructor.`. – br3nt Sep 09 '22 at 06:49
  • `config.DependencyResolver = resolver;` didn't seem to work for me, whereas `System.Web.Mvc.DependencyResolver.SetResolver(resolver);` did – br3nt Sep 12 '22 at 03:01
5

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);
    }
}
  • 4
    You should edit your initial response to include this.... it's a little out of context as a seperate answer. Great work though :) – Lewis Cianci Sep 12 '19 at 00:49
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

LukeP
  • 10,422
  • 6
  • 29
  • 48
Dei Revoledo
  • 175
  • 1
  • 5
3

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.

LoïcR
  • 4,940
  • 1
  • 34
  • 50
  • If NuGet complains about Unity having a dependency on a lower version of Newtonsoft.JSON library, try 2-3 times. I tried installing AutoFac which had same issue but successfully resolved by uninstalling old version and using new. Once it was there, I again tried installing Unity with success and then uninstalled AutoFac (just because of more comfort in Unity :) ) – NitinSingh Jun 11 '17 at 21:49
  • 5
    Not down-voting, but Unity seems abandoned (?) : https://blogs.msdn.microsoft.com/dotnet/2015/08/21/the-future-of-unity/ One comment: "The new owners did almost nothing to keep the project alive. The last commit is almost one year old. The lastest version was released in October 2015. Apparently there has been some work to support .NET Core, but it was never completed. Not a single pull request has ever been merged. And the maintainers don’t even reply to issues and PRs anymore. So, I think it’s official now: Unity is dead…" – iokevins Jul 06 '17 at 16:31
  • 4
    @iokevins Microsoft has a new DI framework for ASP.NET Core. Turns out it can be included with ASP.NET 4 and used in MVC 5. It's not as fully featured as other frameworks but it should handle basic use cases for a project. Otherwise, I agree Unity seems to be dead and other frameworks are where to look to.Here are instructions to bring in the new framework with an MVC5 project: http://scottdorman.github.io/2016/03/17/integrating-asp.net-core-dependency-injection-in-mvc-4/ – Michael Sep 12 '17 at 15:12
  • According to this: https://www.palmmedia.de/Blog/2011/8/30/ioc-container-benchmark-performance-comparison, performance is not fast enough. – jstuardo Apr 29 '20 at 23:51
  • as of January 2022 the latest version of Unity is deprecated – tnJed Apr 20 '22 at 20:07
2

In this video a Microsoft MVP demos dependency injection in MVC5 with AutoFac. Very clear explanation on how to set it up:

Dependency Injection MVC5 Demo

Source code is available on GitHub

Edward Pescetto
  • 856
  • 1
  • 7
  • 16
1

From here https://scottdorman.blog/2016/03/17/integrating-asp-net-core-dependency-injection-in-mvc-4/

this line saved me.

    services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
      .Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
      .Where(t => typeof(IController).IsAssignableFrom(t) 
       || t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
0

I recommend using Windsor, by installing the nuget package Castle Windsor MVC Bootstrapper, then you can create a service that implements IWindsorInstaller, something like this:

public class ServiceRegister : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container,
    Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();

        container.Register(
            Component
                .For<IServiceToRegister>()
                .ImplementedBy<ServiceToRegister>().
             DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
                .LifestyleTransient());
    }
}

Then inside your controller something like this:

public class MyController 
{
    IServiceToRegister _serviceToRegister;

    public MyController (IServiceToRegister serviceToRegister)
    {
        _serviceToRegister = serviceToRegister;//Then you can use it inside your controller
    }
}

And by default the library will handle sending the right service to your controller by calling the install() of ServiceRegister at start up because it implements IWindsorInstaller

Ali Ezzat Odeh
  • 2,093
  • 1
  • 17
  • 17
-1

Having started from this thread to figure out how to use Microsoft.Extensions.DependencyInjection in my ASP.NET MVC 5 project, and reading and trying and failing, I finally came up with a solution that I wanted to shamelessly offer to the rest of you.

I pieced together a gist from David Fowler, the example code from Scott Dorman, and added in a bit of my own spice to create library that allows you to simulate ASP.NET Core's Startup in ASP.NET MVC "Classic".

For more information, please take a look at the GitHub repository for Arex388.AspNet.Mvc.Startup. If you're interested you can also read through my blog post about it, here (if it doesn't load, refresh until it does, the server's been giving me troubles and I haven't had time to investigate...). Hope it helps someone!

Gup3rSuR4c
  • 9,145
  • 10
  • 68
  • 126