Skip to content
Elisenda Gascon By Elisenda Gascon Apprentice Engineer II
Implementing dependency injection in ASP.NET Core

In this post, we will be showing how to implement Dependency Injection in an ASP.NET Core application. We'll start by looking at the problems that arise from not using the inversion of control pattern in an application containing dependencies. After that, we'll see how to modify and refactor our code by using dependency injection and using the dependency injection container, which we will briefly introduce.

An application containing dependencies

Throughout this post, we will be working on a simple ASP.NET Core web application, using ASP.NET Core 6. The aim of the application is to display the current date on the index page. Let's create it in 3 steps.

In this first instance, we'll create the application without using any form of dependency injection. This should not be the preferred method to inject dependencies in an application, as we'll see later.

Step 1: Create an ASP.NET Core web application

First, create a new ASP.NET Core web application. You can do this from the command line by typing the following:

dotnet webapp -o DIContainer_Demo

Step 2: Create a class containing the service we want to use

The aim is to display the current date on the index page of the application. To do this, first create a new class, called ShortDate. This will contain the logic to get the date that we'll later display in the index page.

ShortDate will contain the following code:

public class ShortDate
{
    private readonly string _date;

    public ShortDate()
    {
        _date = DateTime.Now.ToShortDateString();
    }

    public string GetDate()
    {
        return _date;
    }
}

Step 3: Use the ShortDate service in the IndexModel class.

The IndexModel has an OnGet method, which populates the model used to build and return the index page content. This is where we'll call GetDate from the ShortDate class. The IndexModel class has therefore a dependency on ShortDate.

In order to be able to use ShortDate, we need to create an instance of the class. For now, we are doing this inside of the OnGet method. Keep reading to find out the preferred way to do this!

public class IndexModel : PageModel
{
    public string Date { get; set; }

    public void OnGet()
    {
        var _shortDate = new ShortDate();

        var date = _shortDate.GetDate();

        Date = date;
    }
}

The index page displays the date retrieved using ShortDate from the OnGet method:

@page
@model IndexModel
@{
}

<div class="text-center">
    <h4 class="display-5">Date: @Model.Date</h4>
</div>

Showing the index page once we run the application. The current date is shown.

Our application works the way we want to, yet if you're familiar with the concept of Inversion of Control, you will have noticed there is something fundamentally wrong with how our service is instantiated and consumed by the OnGet method in the IndexModel class.

Dependency Injection and Inversion of Control

To populate the page model, the OnGet method calls the GetDate method on ShortDate. This is called a dependency, because the OnGet method in the IndexModel class is dependent on the ShortDate service to be able to run.

Whenever there is a dependency, an instance of the service on which the class depends needs to be created. Currently, the OnGet method is responsible for the creation of an instance of ShortDate, on which it depends. This is an example of tight coupling, as the OnGet method is tightly coupled to the current implementation of ShortDate – a change in the implementation of ShortDate requires a change in the OnGet method. This makes code hard to maintain, as new requirements would need manual updates to potentially several classes.

The aim of dependency injection is to make code more easily maintainable. By using inversion of control, the method that needs an instance of a service is no longer responsible for creating said instance. Instead, the constructor of the class will list in its parameters all the dependencies needed within that class, and inject instances of them when they are needed.

In order to fully implement inversion of control, we need to implement dependency injection by registering the services in the dependency injection container. The framework can then take responsibility for creating and injecting the required dependencies.

The Microsoft dependency injection container is built into ASP.NET Core. It allows services to be registered with the IServiceCollection. When a service registered is requested, the container creates and injects an instance of that service.

Implementing Dependency Injection

Let's see how to implement dependency injection in our application in three steps.

Step 1: Introduce constructor injection

When using constructor injection, the dependencies of a method are passed into the class through the constructor, rather that the method creating those instances itself.

This is the IndexModel class using constructor injection:

public class IndexModel : PageModel
{
    private readonly ShortDate _date;

    public IndexModel(ShortDate date)
    {
        _date = date;
    }

    public string Date { get; set; }

    public void OnGet()
    {
        // We don't need to create an instance of ShortDate inside the method
        // var _shortDate = new ShortDate();

        var date = _date.GetDate();

        Date = date;
    }
}

The changes made to the IndexModel class are:

  • The ShortDate service has been added as a parameter of the constructor.
  • The injected dependency is stored into a private field so that it's accessible by the methods in the class.
  • The instantiation of ShortDate inside the OnGet method is removed. This is not needed as the instance of the service is already being injected by the constructor.
Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

This implements inversion of control, as we are inverting the control of the creation of dependencies. The OnGet method is no longer responsible for creating and passing dependencies. Instead, the constructor in the IndexModel class takes up that responsibility.

Step 2: Create an abstraction of the service

Let's go one step further to fully implement inversion of control by using an abstraction of ShortDate rather than its implementation. This will reduce the coupling between IndexModel and its dependency, ShortDate, and result in more loosely coupled code.

Extract the interface for ShortModel and call it IDate.

public interface IDate
{
    string GetDate();
}

Make sure that ShortDate is implementing IDate if you are doing this manually.

public class ShortDate : IDate
{
    private readonly string _date;

    public ShortDate()
    {
        _date = DateTime.Now.ToShortDateString();
    }

    public string GetDate()
    {
        return _date;
    }
}

Update the IndexModel class to use the interface instead of the implementation.

public class IndexModel : PageModel
{
    private readonly IDate _date;

    public IndexModel(IDate date)
    {
        _date = date;
    }

    public string Date { get; set; }

    public void OnGet()
    {
        // We don't need to create an instance of ShortDate inside the method
        // var _shortDate = new ShortDate();

        var date = _date.GetDate();

        Date = date;
    }
}

Step 3: Register your service in the Dependency Injection container

The last step we need to complete for our application to run without errors is to let the framework know how to resolve the dependency on IDate at runtime.

To do this, register the service in the Dependency Injection container in Program.cs. Note that services are registered in the Program.cs file in ASP.NET Core 6. In earlier versions of ASP.NET Core, this is done within the ConfigureServices method, located in the Startup class.

The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

Registering the service takes only one line of code:

builder.Services.AddTransient<IDate, ShortDate>():

The service is registered in the IServiceCollection, accessed from the builder.

Using the Add extension method on the service collection registers our service with the container.

If we are not registering multiple implementations of the same interface type, where we place our registration is irrelevant as long as it happens before build is called on the builder.

In this registration, we are using the overload of the AddTransient that accepts two generic arguments: the abstract type (also called the service type) and the implementation type. The latter needs to be a class that implements the service type. In this case, we are registering the ShortDate implementation of IDate. To change the implementation used in the instantiation and injection of the service, the only change needed now would be to replace ShortDate for the new implementation.

Note that AddTransient is only one of the three possible lifetimes to register a service, but we'll leave the details on these for a different blog post.

Conclusion

In this post, we looked at how to implement dependency injection in an ASP.NET Core web application. We started with an example of an application where dependency injection hadn't been implemented. We explained how to refactor the code step by step to apply the inversion of control pattern, first using constructor injection, then using an abstraction of the service injected rather than its implementation, and lastly registering the service in the dependency injection container.

Elisenda Gascon

Apprentice Engineer II

Elisenda Gascon

Elisenda was an Apprentice Engineer from 2021 to 2023 at endjin after graduating with a mathematics degree from UCL. Her passion for problem solving drove her to pursue a career in software engineering.

During her time at endjin, she helped clients by delivering data analytics solutions, using tools such as Azure Synapse, Databricks notebooks, and Power BI. Through this, she also gained experience as a consultant by delivering presentations, running customer workshops, and managing stakeholders.

Through her contributions to internal projects, she gaines experience in web development in ASP.NET, contributed to the documentation of the Z3.Linq library, and formed part of a team to develop a report to explore global trends in wealth & health.

During her training as a software engineer, Elisenda wrote a number of blog posts on a wide range of topics, such as DAX, debugging NuGet packages, and dependency injection. She has also become a Microsoft certified Power BI analyst and obtained the Green Software for Practitioners certification from the Linux Foundation.