Dependency Injection in ASP.NET MVC6

project-ara-phone

Dependency injection (DI) has been possible in previous versions of MVC. With each new version DI has been easier to implement and, with MVC6, DI is supplied right out of the box. In this article we’ll look at how the new DI implementation works, what are its weaknesses and how we can replace it with our favorite DI framework.

What’s new

The unification of APIs across ASP.NET is a common theme throughout ASP.NET 5, and dependency injection is no different. The new ASP.NET stack including: MVC, SignalR and Web API, etc. rely on a built-in minimalistic DI container. The core features of the DI container have been abstracted out to the IServiceProvider interface and are available throughout the stack. Because the IServiceProvider is the same across all components of the ASP.NET framework a single dependency can be resolved from any part of the application.

The DI container supports just 4 modes of operation:

  • Instance – a specific instance is given all the time. You are responsible for its initial creation.
  • Transient – a new instance is created every time.
  • Singleton – a single instance is created and it acts like a singleton.
  • Scoped – a single instance is created inside the current scope. It is equivalent to Singleton in the current scope.

Basic setup

Let’s walk through setting up DI in a MVC application. To demonstrate the basics, we’ll resolve the dependency for the service used to get project data. We don’t need to know anything about the service other than that it implements the IProjectService interface, an interface custom to our demo project. IProjectService has one method, GetOrganization(), that method retrieves an organization and its corresponding list of projects.

public interface IProjectService
{
    string Name { get; }
    Organization GetOrganization();
}

public class Organization
{
    public string Name { get; set; }
    [JsonProperty("Avatar_Url")]
    public string AvatarUrl { get; set; }
    public IQueryable<Project> Projects { get; set; }
}

We’ll use the IProjectService to get the organization data and display it in a view. Let’s start by setting up the controller where the service will be used. We’ll use constructor injection by creating a new constructor method for our controller that accepts an IProjectService. Next, the Index action will call GetOrganization, sending the data to the view to be rendered.

private readonly IProjectService projectService;
public HomeController(IProjectService projectService)
{
    this.projectService = projectService;
}
public IActionResult Index()
{
    Organization org = projectService.GetOrganization();
    return View(org);
}

If we try to run the application at this point we’ll receive an exception because we haven’t yet added a concrete implementation of our IProjectService to the DI container.

InvalidOperationException: Unable to resolve service for type 'DependencyInjectionMVC6Demo. Services. IProjectService' while attempting to activate 'DependencyInjectionMVC6Demo. Controllers. HomeController'.
Microsoft. Framework. DependencyInjection. ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)

The exception message shows that the code fails during a call to ActivatorUtilities.GetService. This is valuable information because it shows that in MVC6 the DI container is already involved in the controller’s construction. Now we just need to tell the container how to resolve the dependency.

In order to resolve the dependency, we need a concrete implementation of IProjectService. We’ll add a DemoService class and, for simplicity, it will use static dummy data.

public class DemoService : IProjectService
{
    public string Name { get; } = "Demo";

    public Organization GetOrganization() => new Organization
    {
        Name = this.Name,
        AvatarUrl = $"http://placehold.it/100&text={this.Name}",
        Projects = GetProjects()
    };

private IQueryable<Project> GetProjects() => new List<Project> {
         new Project {
             Id = 0,
             Description = "Test project 0",
             Name = "Test 0",
             Stars = 120
         },
         //...
         new Project {
             Id = 4,
             Description = "Test project 4",
             Name = "Test 4",
             Stars = 89
         }
    }.AsQueryable();
}

Finally, we’ll instruct the DI container to instantiate a new DemoService whenever IProjectService is required. To configure the container we’ll modify the ConfigureServices method in Startup.cs. Our configuration will be added to the end of this method.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    //... other services
    // Add MVC services to the services container.
    services.AddMvc();

    //our services
}

The service is added by using the AddTransient extension method on the services collection, and setting the IProjectService as the type of service and the DemoService as the implementation.

public void ConfigureServices(IServiceCollection services)
{
    //... other services
    // Add MVC services to the services container.
    services.AddMvc();

    //our services
    services.AddTransient<IProjectService, DemoService>();
}

With the service added, DemoService will now be instantiated when the controller is created, and the exception will no longer be thrown.

demo-service

Weaknesses

expectations-real

Having a baked in DI layer throughout the ASP.NET stack is helpful, but you should temper your expectations. While it is useful for simple scenarios, it is very limited. The default container only supports constructor injection and it can only resolve types with one, and only one, public constructor. There’s no need to worry though, you have complete control over the DI container and it’s easy to replace with your favorite DI solution.

Replacing default MVC6 DI container

Setting up a replacement DI container does not require a lot of code, but the process could be more discoverable. We’ll continue with our previous example to show exactly where the extensibility point is.

The method we are using, ConfigureServices, is actually one of two delegates the application will use to register the DI container and its services. The first, and default, delegate is an Action<IServiceCollection>, which was used in the previous example to resolve our DemoService. The second is a Func<IServiceCollection, IServiceProvider>, which is used to replace the default container by returning a custom implementation IServiceProvider.

To change from the default and allow an alternative IServiceProvider to be used, we’ll need to change the method signature. We can test this easily by modifying our example, we’ll change the method from returning void to IServiceProvider, then at the very end of the method return services.BuildServiceProvider(). At this point the method does exactly the same thing as before – we’re still using the default DI container.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //... other services
    // Add MVC services to the services container.
    services.AddMvc();

    //our services
    services.AddTransient<IProjectService, DemoService>();
    return services.BuildServiceProvider();
}

The application will still build and run just as it did before, except now we’ve exposed the extensibility point we need.

Next, we need to add a third party DI container. There are a variety of DI containers to choose from, all with their own strengths and weaknesses. For this example we’ll be using Autofac. At the time of writing, Autofac has a DNX compatible alpha version we can use.

To install Autofac we’ll open our package.json file and add the Autofac alpha binaries.

"dependencies": {
    ...
    "Autofac": "4.0.0-alpha2",
    "Autofac.Dnx": "4.0.0-alpha2"
}

In the Startup.cs, we’ll modify the ConfigureServices method again. This time, we’ll add our dependencies to Autofac and resolve the container back to an IServiceProvider replacing the default container.

public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        //... other services
        // Add MVC services to the services container.
        services.AddMvc();

        //Autofac config
        var builder = new ContainerBuilder();

        builder.RegisterType<DemoService>()
            .As<IProjectService>().InstancePerLifetimeScope();

        //Populate the container with services that were previously registered
        builder.Populate(services);

        var container = builder.Build();

        return container.Resolve<IServiceProvider>();
}

Now our application is using the Autofac container in place of the default container. We can fully take advantage of Autofac’s features and, because ASP.NET was developed against the IServiceProvider, we wont need to change any other code in our project.

Let’s make one final change to the example by swapping our DemoService to another implementation that uses GitHub’s REST API to populate our project list. The GitHubService implements the same IProjectService as DemoService, however it takes a name parameter in its constructor. Using Autofac we can set the Name value of the constructor in the configuration.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //...
    builder.Register(svc => new GitHubService("Telerik"))
        .As<IProjectService>().InstancePerLifetimeScope();

    //builder.RegisterType<DemoService>()
    //  .As<IProjectService>().InstancePerLifetimeScope();
}

Restarting the application reveals that our new GitHubService was successfully resolved without touching our controller code.

github-service

Wrap up & Resources

The new ASP.NET provides out-of-the-box DI throughout the stack. The new DI container is basic enough to get the job done, but lacks robust configurations. The container is easy enough to replace if you know were to look, allowing you to use fully-featured, third party tools. The source code for both the framework and the examples for this project can be found out GitHub.

MVC 6 Changes Every Developer Should Know

Comments

  • Pingback: Dew Drop – June 8, 2015 (#2030) | Morning Dew()

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1878()

  • Don’t create the interface IProjectService unless you’re going to have multiple implementations and will select one with the strategy pattern or execute across an IEnumerable. Creating IProjectService for any other reason is just noise pollution. There is no purpose to it.

    You do not need IProjectService for testing. All you need is to mark a method virtual and any mocking framework can overwrite it or you can manually mock/stub it if you so desire for whatever reason.

    Following my statements would have ended this blog post at:

    InvalidOperationException: Unable to resolve service for type ‘DependencyInjectionMVC6Demo. Services. IProjectService’ while attempting to activate ‘DependencyInjectionMVC6Demo. Controllers. HomeController’. instead of the like 1000 lines of text after this.

    If you had just injected DemoService you would have just been done.

    • … dude, I think you have completely missed the point of this article.

      • ProjectService : IProjectService is terrible code. I make sure to call this out in every article i see. Too many people only see this pointless activity and think this what you must do for the DIP.

        • Odessa Silverberg

          Dude, you are one of the worst developers I’ve seen. I would never hire you into my company, for your complete ignorance to understand the need for it. You should just quit your job and do something else, as developer you have failed hard

    • ardalis

      What about if you declare IService in a Core project and implement it in a separate Infrastructure project, to enforce separation of concerns and keep your UI project from depending (directly) on Infrastructure? At that point, you have no choice but to use an interface (or at least, a separate base type). I can see your point in small demo apps with one project, though, but in most “real” multi-project applications I think having a separate interface makes sense (and adds minimal additional code – most interfaces should be under 10 lines of non-executable code).

      • There’s still no reason at all to create the interface if you won’t have multiple implementations of it at the same time. (Test harnesses are not an implementation)

        • ardalis

          Ok, how would you do this, then?
          In Core project, a FooService uses (some type) to get some data. It does some work on that data, then returns a result.
          Ideally, the Core project shouldn’t know about where the data comes from – its implementation should be in the Infrastructure project. Infrastructure depends on Core.
          Without using an interface (or equivalent), how does FooService work with its collaborator in a loosely coupled fashion?

          • Following your core project label I would expect there to be multiple implementations of ISomeThing and then be fine.

            This could also be solved by making the core expose that portion along the lines of FooService<T, TU>(Func<T> getData1, Func<TU> getData2)

            Or you could make an actual object be the contract and require consumers to map to the object to pass into the core project.

            Going back to realism, i don’t think there’d be many situations where i want to inject local implementations back up into the core. So in this scenario i would likely choose the Funcs or a DTO to be the contract.

  • Hi Ed, good article, I mean the purpose of them is show that in ASPNET 5, its not necesary to use a DI Container when I want to use DI, but the article only show the feature, not best practices dotnetchris (I agree with you comment)

    Also, I would like to make a clarification when you say: “The default container only supports constructor injection…” this part is no true, its possible to do property and action injection using [FromServices].

    e.g.

    [FromServices]
    public IProjectService projectService {get;set;}

    or

    public IActionResult Index([FromServices] IProjectService projectService)
    {
    Organization org = projectService.GetOrganization();
    return View(org);
    }

    Regards,

    • [FromServices] is special to MVC, and it cannot be used everywhere. When the action activator is creating the action, it takes an [FromServices] attributes, and requests an instance from the container and then returns the value.

      Again this is a special case to MVC6 and won’t work outside controllers (in your dependent services for example).

      • Yes @disqus_8JdKlAdXdW:disqus, you’re right! My point is only to do an aclaration, but yes, DI is not only required at controllers, other application’s part need it, and in those case it’s necessary to use a DI Container or other tecnic.
        Thanks David, regards!

  • I would also keep in mind, that as a developer you should never use IServiceProvider by itself, instead you should always get your dependencies from your constructor if at all possible.

  • Pingback: MVC6 New Project Tour -Telerik Developer Network()