Dependency Injection in ASP.NET Core

In this article, we are going to learn what is Dependency injection and types of it along with different Service lifetimes which we use to register service in the container.

What is Dependency injection?

Dependency injection is a design pattern which is used to remove hard code dependency from application in ASP.NET Core Dependency injection is the first-class citizen; it is built into the framework. In older version of ASP.NET MVC, we use to install Dependency injection framework from NuGet’s such as Unity, Autofac, Ninject, simple injection here we need to configure it for using but in ASP.NET Core we are ready to use its small configuration in Startup Configure Service method.

Mostly we use to create an object in an application where ever we need which lead to increase in maintenance work, in dependency injection you can register all dependency at once place which is known as container and wherever you require an instance of object ask for it don’t create object, container create object and inject it.

The core intention behind dependency injection is to achieve a Separation of Concerns.

3 Different Ways to Inject dependency
1 Constructor Injection
In Constructor Injection, the dependency is injected through the constructor of a class.
2 Method Injection (FromServices attribute)
In Method Injection the dependency is injected directly to Action Method using FormService Attribute
3 Dependency injection into views (@inject directive)
A Service can be injected into a view using the @inject directive.
Before moving ahead, let’s get clear View what Service lifetimes are.

After understanding the definition let’s implement it and understand in detail how to use it and where to use it.

We are going to create ASP.NET Core MVC Application with the name ‘DemoTypesofDependencyInjection‘.

Service lifetimes

Whenever we register a service in a container, we need to set a lifetime for it.
There are three ways we can register service in a container

  1. Transient services
    In Transient services, it creates a new instance on each time you request.
  2. Scoped services
    In Scoped services, it creates a new instance once per request within the scope.
    (Single Http request is one Scope)
  3. Singleton
    In Singleton services, it creates instance only once and reuses it for all request.

Let’s create a simple application and learn how to configure and inject dependency in simple steps.

We are going to develop ASP.NET Core MVC Application with the name ‘DemoDependencyInjection‘.

After creating Application, we are going to add Folder to project with Name ‘CustomServices‘ inside that folder we are going to Add TimerService Class.

Added Class RandomNumberService

We have created a class with name RandomNumberService which will return a random string.
Every time this service is requested in the constructor it stores a new random string in ‘_randomValue‘ variable and when we call ShowRandomString Method, it will return random string.
For creating a random string, we have used the RNGCryptoServiceProvider class.

using System;
using System.Security.Cryptography;
using System.Text;

namespace DemoDependencyInjection.CustomServices
{
    public class RandomStringService
    {
        private readonly string _randomValue;
        public RandomStringService()
        {
            _randomValue = RandomString(10);
        }
        public string ShowRandomString()
        {
            return _randomValue;
        }

        private static string RandomString(int length)
        {
            const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            StringBuilder res = new StringBuilder();
            using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
            {
                byte[] uintBuffer = new byte[sizeof(uint)];

                while (length-- > 0)
                {
                    rng.GetBytes(uintBuffer);
                    uint num = BitConverter.ToUInt32(uintBuffer, 0);
                    res.Append(valid[(int)(num % (uint)valid.Length)]);
                }
            }

            return res.ToString();
        }
    }
}

Now we have Created ‘RandomStringService‘ for demo let’s create a Middleware where we are going to call RandomStringService.

Added Custom Middleware (RandomStringMiddleware) and Using RandomStringService in it

In this part, we are going to add a custom Middleware with Name RandomStringMiddleware and this middleware uses RandomStringService to display random string, here we are going to write a response of random string which we are going to receive from RandomStringService.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace DemoDependencyInjection.CustomServices
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions 
       package into your project
    public class RandomStringMiddleware
    {
        private readonly RequestDelegate _next;

        public RandomStringMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext, RandomStringService randomStringSService)
        {
            await httpContext.Response.WriteAsync($"Random Value From RandomStringMiddleware 'Transient' :- {randomStringSService.ShowRandomString()}  ");

            await _next(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class RandomStringMiddlewareExtensions
    {
        public static IApplicationBuilder UseRandomStringMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RandomStringMiddleware>();
        }
    }
}

After adding custom Middleware and using RandomStringservice in it next, we are going to Using same RandomString Service in HomeController.

Calling RandomStringService in Home Controller

In this part we already had Home Controller we have made few changes in it, in the constructor we have called RandomStringService which will be resolved by DI container and method inside this service is called in Index action method where we are going to write a response of random string which we are going to receive from RandomStringService.

using DemoDependencyInjection.CustomServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace DemoDependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private readonly RandomStringService _randomStringService;
        public HomeController(RandomStringService randomStringSService)
        {
            _randomStringService = randomStringSService;
        }

        public IActionResult Index()
        {
            HttpContext.Response.WriteAsync($"Random Value from HomeController 'Transient' :- {_randomStringService.ShowRandomString()}  ");
            return View();
        }
    }
}

Registering Service as Transient in ConfigureServices method and Middleware in Configure

Here we are going to register RandomStringService and RandomStringMiddleware in Startup class as shown below.

Configure RandomStringService in ConfigureServices Method

services.AddTransient<RandomStringService>();

Configure TimerMiddleware in Configure Method

app.UseRandomStringMiddleware();

Code Snippet

using DemoDependencyInjection.CustomServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DemoDependencyInjection
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // ########## Registered Service as Transient ##########
            services.AddSingleton<RandomStringService>();
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // ########## Calling RandomStringMiddleware  ##########
            app.UseRandomStringMiddleware();

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

After registering service as Transient next, we are going to run the application and see what values are displayed on a browser.

In Transient services, it creates a new instance on each time you request. If you see the values displayed on a browser the first request is handled by middleware which writes random string as response and the further request is handled by homecontroller which also writes random string as a response but both random string values are different because the new instance is created on each time you request.

Next, we are going to registering Service as Scoped and see how it works.

Registering Service as Scoped in ConfigureServices method

In this part, we are going to register RandomStringService as Scoped in ConfigureServices Method and also, we are going to changes response message from ‘Transient’ to ‘Scoped‘ in Service and Middleware.

Below is the way we are going to register service as scoped.

services.AddScoped<RandomStringService>();

Code Snippet of Startup Class

using DemoDependencyInjection.CustomServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DemoDependencyInjection
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // ########## Registered Service as Scoped ##########
            services.AddScoped<RandomStringService>();
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // ########## Calling RandomStringMiddleware  ##########
            app.UseRandomStringMiddleware();

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

After registering service as Scoped next, we are going to run the application and see what values are displayed on a browser.

In Scoped services, it creates a new instance once per request within the scope (Single Http request is one Scope). If you see the values displayed on a browser the first request is handled by middleware which writes random string as response and the further request is handled by homecontroller which also writes random string as a response but both random string values are same because for single HTTP request is considered as one scope.

Request -> Middleware -> Controller (one scope)

Registering Service as Singleton in ConfigureServices method

In this part, we are going to register RandomStringService as Singleton in ConfigureServices Method and also, we are going to changes response message from ‘Scoped’ to ‘Singleton’ in Service and Middleware.

Below is the way we are going to register service as Singleton.

services.AddSingleton<RandomStringService>(); 

Code Snippet of Startup Class

using DemoDependencyInjection.CustomServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DemoDependencyInjection
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // ########## Registered Service As Singleton ##########
            services.AddSingleton<RandomStringService>();
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // ########## Calling RandomStringMiddleware  ##########
            app.UseRandomStringMiddleware();

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

After registering service as Singleton next, we are going to run the application and see what values are displayed on a browser.

In Singleton services, it creates instance only once and reuses it for all request. If you see the values displayed on a browser the first request is handled by middleware which writes random string as response and the further request is handled by homecontroller which also writes arbitrary string as a response but both random string values are same because it only created a single instance for the whole application. If you keep the refreshing page, the random string values will not change.

By Saineshwar Bageri

I am Microsoft MVP | C# Corner MVP | Code Project MVP | FULL STACK .NET Developer and working on .Net Web Technology (Asp.net, Asp.net Core,.Net Core, C#, Sqlserver, MVC, Windows, Console Application, javascript, jquery, json, ORM Dapper) and also a freelance developer.