ASP.NET Core: Dependency Injection and Services in MVC

4.71 (34 votes)

This is the fourth in a series of articles that explores ASP.NET Core by reconstructing the Visual Studio 2015 Web Application template from an Empty template. This article looks at the role of services and the new ASP.NET Core dependency injection system. The series of articles has been developed using Visual Studio RTM and ASP.NET Core Beta 6. It will be kept updated along with newer releases.

Before I discuss the ASP.NET Core dependency injection system, I feel it is important to take a bit of time to try to illustrate the problem that Dependency Injection is designed to solve. To do that, I have had to move the Empty template on a bit from where I left off in the last article. This has involved adding some standard MVC stuff to the project, including the HomeController and its associated Views, along with the site Javascript and CSS files from the Web Application template. I also had to add a couple of extra packages to the project to enable the serving of static files and to cater for the TagHelpers used in the views.

The Problem

A common pattern within web development - especially among beginners - is to pile a whole lot of code that does everything that a web page needs (data, emailing, error handling, logging, authentication etc) into one place. In the Microsoft world, this practice has evolved from spaghetti code in a classic ASP code file, to the code behind file for a web form, and then to a controller in an MVC application. Take a look at the following:

[HttpPost]
public async Task<ActionResult> Contact(ContactViewModel model)
{
    using (var smtp = new SmtpClient("localhost"))
    {
        var mail = new MailMessage
        {
            Subject = model.Subject,
            From = new MailAddress(model.Email),
            Body = model.Message
        };
        mail.To.Add("Support@domain.com");
        await smtp.SendMailAsync(mail);
    }
    return View();
}

This is an example of the type of code that can be found in many demo samples (including some on my site). It features a controller action method that responds to a POST request and generates and sends an email message from the posted form values represented as a view model. So what's wrong with it?

Dependencies

The main problem is that the controller class has a responsibility for something which is not part of its primary concern. The code that manages the emailing is a dependency of the controller class. This potentially introduces the following problems:

  • If you want to change your emailing system over to e.g. Exchange Web Services, you have to make changes to the controller class . This violates the Single Responsibility Principal. The controller class should only be responsible for martialling data for views. It should not be responsible for sending email. Changing the code concerning one of the controller class's responsibilities could result in bugs being introduced into its other responsibilities.
  • If you have similar emailing functionality in multiple places in the application, you have to make the same change multiple times. The more places you have to make changes in, the more chance there is of you introducing other bugs to other parts of the application - or overlooking one or more areas where the change needs to be applied.
  • If you want to unit test your controller's Contact method (to ensure it returns the correct View, for example), you will not be able to do so without generating an email. This will require communication with outside processes, which will slow tests down.
  • Your test may fail because of some failure in the mail sending routine rather than any logical failure in view selection (which is what a unit test is supposed to cover).

Good design practice recommends that you create specific components or services to cover discrete and separate pieces of functionality (or separate your concerns), and then call upon these services where needed. In this example, the code concerned with mailing should be separated out into its own component which will expose a public method that can be called from within the controller. This is how the code might be refactored to create a separate class called EmailSender, which is responsible for the actual sending of the email:

using System.Net.Mail;
using System.Threading.Tasks;

namespace WebApplication2.Services
{
    public class EmailSender
    {
        public async Task SendEmailAsync(string email, string subject, string message)
        {
            using (var smtp = new SmtpClient("localhost"))
            {
                var mail = new MailMessage
                {
                    Subject = subject,
                    From = new MailAddress(email),
                    Body = message
                };
                mail.To.Add("Support@domain.com");
                await smtp.SendMailAsync(mail);
            }
        }
    }
}

Note that this is purely for illustration only. A real email component will have a lot of validation and error handling included. And this is how the EmailSender component is consumed within the controller action:

[HttpPost]
public async Task<ActionResult> Contact(ContactViewModel model)
{
    EmailSender _emailSender = new EmailSender();
    await _emailSender.SendEmailAsync(model.Email, model.Subject, model.Message);
    return View();
}

This is better, because now the controller has no knowledge of how emails are sent. As far as the controller is concerned, the EmailSender class is a black box that encapsulates the details of how emails are generated and dispatched. All the controller class needs to know is that the EmailSender class offers a SendEmailAsync method that accepts some strings. If you want to change anything to do with sending emails, you change the EmailSender class/component only. You don't have to change the controller code at all. This eliminates the possibility of new errors being introduced into the controller class when updates to emailing routines are being made. It also saves time as you don't have to make identical changes in any other places where email is sent.

But there is still a problem. The controller is still dependent on (tightly coupled with) a specific (or concrete) type of email component. When you test the controller action, an email will still get sent. You could makes changes to the code to replace the EmailSender class with a MockEmailSender class that doesn't actually do anything whenever you want to run your tests. However, having to do that in multiple places, and for all the other services that rely on outside processes (logging, data access etc.) every time you wanted to run your tests, and then reversing all the changes afterwards is not a practical solution. The reality is that you are unlikely to bother to execute your tests at all.

Dependency Injection

Dependency injection (DI) is a design pattern. The problem it attempts to address is the one outlined above, where a class is tightly coupled to a specific implementation of a service. DI offers one way to loosen that coupling by having the service injected into the dependent class. You can do this in a number of ways. You can inject the service into the class's constructor or into one of its properties using the setter. Or, rather than inject a specific type of the service (EmailSender or MockEmailSender or ExchangeEmailSender) you inject an abstraction that represents the service. The abstraction is defined in an interface. In this very simple example, the interface specifies one method:

public interface IEmailSender
{
    Task SendEmailAsync(string email, string subject, string message);
}

The IEmailSender interface specifies a pattern (contract is another term for the same thing) that any type that wants to be seen as an IEmailSender must conform to in order for it to comply. Currently, that means that the type must implement a SendEmailAsync method that takes three strings as specified by the interface. The existing EmailSender class already meets that requirement, so now it just needs to formally implement the interface to become an IEmailSender. This is achieved by adding a colon after the class name followed by the name of the interface that it should implement:

public class EmailSender : IEmailSender
{
    public async Task SendEmailAsync(string email, string subject, string message)
    {       
    .... 

The next iteration of the controller sees the EmailSender class injected via the class constructor. It is saved to a private backing field for use wherever required within the controller:

public class HomeController : Controller
{
    IEmailSender _emailSender;
    public HomeController(EmailSender emailSender)
    {
        _emailSender = emailSender;
    }
        
    .... 
 
    [HttpPost]
    public async Task<ActionResult> Contact(ContactViewModel model)
    {
        await _emailSender.SendEmailAsync(model.Email, model.Subject, model.Message);
        return View();
    }
    ....

The truth is that you can stop refactoring at this point. This is an example of dependency injection - known by the slightly pejorative term "poor man's dependency injection". The code within the controller follows the maxim "program to an interface", which decouples the controller from a specific implementation (apart from in its constructor), and enables separation of concerns. If you have no plans to unit test your code, and you can see no good reason for changing the implementation of your IEmailSender at the moment (i.e. it is not part of the requirement spec to do so) then you can go on your merry way and this code is just fine. However, you might want to continue reading to find out how to further decouple the dependency, and to learn about the dependency injection system in ASP.NET Core.

The following code shows how you replace the concrete implementation of the EmailSender class with the interface in the controller class, removing all dependencies on EmailSender:

public class HomeController : Controller
{
    IEmailSender _emailSender;
    public HomeController(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }
        
    .... 
 
    [HttpPost]
    public async Task<ActionResult> Contact(ContactViewModel model)
    {
        await _emailSender.SendEmailAsync(model.Email, model.Subject, model.Message);
        return View();
    }
    ....

A private field is added to the constructor class. The data type for the field is IEmailSender, and it is used to store the instance of the IEmailSender that is injected via the constructor that has also been added to the class. That instance is then referenced in the Contact method as before. However, if you try to run the application at the moment, it will generate an exception

MVC Core  and EF 7

The exception is generated because, at the moment, there is no way for the application to know what concrete type IEmailSender should be resolved to. You cannot instantiate an interface and start calling methods on it. It's an abstraction and the methods it defines have no implementation. You can see from the message above that the exception is generated by the new Dependency Injection framework, and it is this that needs to be told what to use whenever it encounters IEmailSender. You do this by adding a registration to a dependency injection container.

DI Containers at their simplest are little more than dictionary-like structures that store the abstraction and the concrete type that should be invoked wherever the abstraction used. Most DI containers offer far more functionality than that, but, at their core, that's all they are - a kind of look-up table.

Dependency Injection is not new in ASP.NET MVC. People have been doing it for years and using a variety of third party DI containers to manage the the resolving of types. What is new in MVC 6 is that a very basic DI container is included as part of the framework. It is minimalistic and doesn't cover advanced use cases, but should be adequate for most common scenarios.

In the preceding tutorial, I registered MVC as a service with the DI container using the AddMvc extension method in the Startup class's ConfigureServices method. This is where you will generally register other services , either explicitly, or through extension methods. The following line of code shows how to register the EmailSender class explicitly:

services.AddTransient<IEmailSender, EmailSender>();

This is the simplest method for adding services to the application - specifying the service as the first parameter and the implementation as the second. If you set a breakpoint on the ConfigureServices method and explore the services, you can see that a lot are already registered with varying lifetime values:

MVC Core and EF 7

The EmailSender was registered with Transient scope via the AddTransient<TService, TImplementation> method. Services registered with Transient scope are created whenever it is needed within the application. That means that a new instance of the EmailSender class will be created by the dependency injection framework every time the Contact method is executed. Two other Lifetime values can be seen in the image: Singleton and Scoped. Singletons are registered via the AddSingleton method and will result in one instance of the service being created on application start and being made available to all requests thereafter. Items added with a Scoped lifetime via the AddScoped method are available for the duration of the request. There is also an AddInstance method that enables you to register a singleton, but you are responsible for creating the intance rather than leaving it to the DI system.

As I said previously, the built in dependency injection system is quite light on features. Each item has to be registered manually. It is expected that the developers of existing, more advanced dependency injection containers will undertake the work required to make their systems compatible with the requirements of ASP.NET Core to enable easy use of them instead of the default container.

Summary

This article began by taking a look at the need for dependency injection in an ASP.NET MVC application, and then explored the ASP.NET Core dependency injection system and covered the creation, registration and consumption of a simple service.

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 57098

13 Comments

- Vaclav Elias

Hi Mike, this is very nicely explained and elaborated from non dependency to dependency on real example. I am not experienced with DI so have got question. You mentioned a few times "If you want to unit test your controller's Contact method.. ..you will not be able to do so without generating an email" but even after your last example you will still generate the email?

I guess at least you would change ONLY in one place services.AddTransient<IEmailSender, EmailSender>(); some dummy MockEmailSender which doesn't change email instead across whole project?

Thanks.

- Mike

@Vaclav

Yes. Your test would use a mock. Then the controller code will execute the SendMailAsync call against something that does nothing.

- Franco

Hi.

In the case of Onion architecture, would you recommend putting the DI resolution in a separate project, "in the edges of the onion"?
Like that, I can make references towards the center, since DI surely will act over the whole solution.

Thanks

- Mike

@Franco,

I am not at all familiar with Onion Architecture.

- Evan

Was this supposed to be in the Summary: This article has taken a closer look at configuration within an ASP.NET 5 application. It showed how to register a JSON file and how to reference a section of it in a strongly typed way. ???

I didn't quite follow what the difference between a poor man DI and using a DI container were.

- Mike

@Evan,

No, that was not supposed to be included.

If you use poor man's DI, you inject concrete implementations of your service rather than abstractions (interfaces). If you want to change the implementation, you have to make changes to the constructor of the controller(s) where you injected it. If that's only on one or two places, usually there is no problem and the poor man's approach is perfectly acceptable.

Something like logging may be injected all over the app. It's much easier to change the implementation if you use a DI container. You simply change the type that the abstraction resolves to in the DI configuration.

- João

Hi Mike!
Just wondering if .NET 5 DI supports name/key resolution.
ie, make one interface resolve two different classes according to a name/key on config or anywhere else.

Thanks!

- Mike

@João

Not as far as I know.

- chujanen

Thanks so much for posting this Mike. The way I would like to organize the code is to inject this service to the view model, but since the ASP.NET mode binder is handling it, it doesn't seem possible.

To morph your example... the ContactViewModel should have a constructor with a parameter of IEmailSender. That breaks things though because ASP.NET MVC won't map the postback to the action method Contact(ContactViewModel model) if there isn't a parameterless constructor for ContactViewModel.

Any ideas on how you would do such a thing?

I thought there would be two options (custom model binding or adding DI to the view model layer, but haven't found any good leads on those approaches).

Thanks!

- Mike

@chujanen

I can't think of any good reason to inject the service into a viewmodel. I wouldn't do such a thing, or spend any time considering how it could be done.

- Larry Watson

Mike,
Your blog is becoming 'goto' for me - thanks.
Most examples I see show DI in the web application. Suppose the web app has reference to a Service Layer which in turn has reference to a DAL layer with the context and repositories. Shouldn't the Service layer deal with its own DI for IRepository? For the web app to do this it has to have reference to the DAL layer which it should be ignorant of.s
In Asp.Net5 I am having difficulty enough on referencing these layers let alone manage DI in them.
Can you point me at anything that covers this?

Best Regards,

- Ashish

Thank you very much Mike. Your article helped me a lot after searching for so long about DI.
Thanks again!

- kapo

What a great article! Been starting to work with .NET again after more than 2 years & all this new DI management was giving me some headache. I've read quite a few articles about it before but none of them were so nicely put. Will forward anyone interested in this here. Thanks again!

Recent Comments

Sivu 19/10/2016 08:21
In response to Entity Framework Core TrackGraph For Disconnected Data
Oh that's very very very nice ! Thanks for the write up Mike, much appreciated for the taking the to...

Mark 12/10/2016 16:42
In response to ASP.NET Web Pages vNext or Razor Pages
Although "Web Pages" was removed from the roadmap, has it just been renamed to "Razor Pages"?...

Satyabrata 12/10/2016 09:20
In response to Entity Framework Core TrackGraph For Disconnected Data
Nice article. Please write more articles featuring ASP.Net web pages. Thank you...

Julian 26/09/2016 14:27
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Fantastic, many thanks Mike! Had got half way down this road before finding your article - saved...

Abolfazl Roshanzamir 14/09/2016 05:36
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Nice article. Thanke you so much ....

cyrus 02/09/2016 15:12
In response to ASP.NET Web Pages vNext or Razor Pages
I've got some news. As Damian stated in this link: https://github.com/aspnet/Mvc/issues/5208 “We...

Simon 01/09/2016 08:00
In response to Loading ASP.NET Core MVC Views From A Database Or Other Location
Thanks Mike, nice post and exactly what I was looking for. Like you said, I think I'll opt to the...

dave 20/08/2016 14:57
In response to ASP.NET Web Pages vNext or Razor Pages
Do SimplemembershipProvider in viewpages is supported?...

Steven 18/08/2016 04:40
In response to Entity Framework Code First and Stored Procedures
Can you provide the directives (using statements) you're using for EF7 example?...

yousaid 17/08/2016 22:08
In response to ASP.NET Web Pages vNext or Razor Pages
Increasingly, learning a Microsoft tool is no longer worth the return on investment. Too many tools...