Dependency Injection In .NET Core

What is Dependency Injection?

Dependency injection (DI) is a software design pattern that allows us to separate the dependencies of a class from its implementation. This makes our code more loosely coupled and easier to test.

In .NET Core, dependency injection is implemented using the IServiceProvider interface. This interface provides a way to register services with the DI container and then inject those services into classes that need them.

To use dependency injection in .NET Core, we need to do the following:

  1. Create an interface that defines the contract for our dependency.
  2. Create a class that implements the interface.
  3. Register the service with the DI container.
  4. Inject the service into the class that needs it.

Implementing Dependency Injection In .NET Core

Here is an example of how to use dependency injection in .NET Core:

Consider a scenario where you want to fetch all the categories from the database and want to show that in the UI layer. So, you will create a service, i.e., a Web API which will be called by the UI layer. Now, in API, we need to create one GET method, which will call the repository, and the repository talks with the database. In order to call the repository, we need to create an instance of the same in the API GET method, which means it’s mandatory to create an instance of the repository for API. We can say the instance of the repository is the dependency of API. Let’s see how we can inject this dependency into our core Web API.

Open Visual Studio and create a new project

Select API as a template and press OK.

Dependency Injection In .NET Core 

As we will fetch the categories, let’s create a category model with two fields - CategoryId and CategoryName. 

namespace DIinCore
{
    public class Category
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
    }
}

Create an interface of the repository having the GetCategories method, which returns the list of category objects.

using System.Collections.Generic;

namespace DIinCore
{
    public interface ICategoryRepository
    {
        List<Category> GetCategories();
    }
}

Implement the preceding interface and return some sample data. As our target is to understand dependency injection, here, we are not going to fetch the data from the database but rather returning hard coded ones.

using System.Collections.Generic;

namespace DIinCore
{
    public class CategoryRepository : ICategoryRepository
    {
        public List<Category> GetCategories()
        {
            List<Category> categories = new List<Category>();

            Category category = new Category() { CategoryId = 1, CategoryName = "Category1" };
            categories.Add(category);

            category = new Category() { CategoryId = 2, CategoryName = "Category2" };
            categories.Add(category);

            return categories;
        }
    }
}

Assume that we are not aware of the dependency injection. Then, how will we expose the GET method from API? We used to create an instance of CategoryRepository and call the GetCategories method using that instance. So tomorrow, if there is a change in CategoryRepository, it will directly affect the GET method of API as it is tightly coupled with that.

[HttpGet]
public async Task<IActionResult> Get()
{
    CategoryRepository categoryRepository = new CategoryRepository();
    List<Category> categories = categoryRepository.GetCategories();

    return Ok(categories);
}

With the .NET Framework, we used to use containers like LightInject, NInject, Unity, etc. But in .NET Core, Microsoft has provided an in-built container. We need to add the namespace, i.e., Microsoft.Extension.DependencyInjection.

So, in the startup class, inside the ConfigureServices method, we need to add our dependency into the service collection, which will dynamically inject whenever and wherever we want in the project. Also, we can mention which kind of instance we want to inject - the lifetime of our instance.

Transient

It creates an instance each time they are requested and are never shared. It is used mainly for lightweight stateless services.

Singleton

This creates only single instances which are shared among all components that require it.

Scoped

It creates an instance once per scope, which is created on every request to the application.

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

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

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Registering a singleton service
            services.AddSingleton<ICategoryRepository, CategoryRepository>();

            // Other dependency injections can be added here using AddTransient or AddScoped
            // services.AddTransient<ICategoryRepository, CategoryRepository>();
            // services.AddScoped<ICategoryRepository, CategoryRepository>();

            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}

So far, we have added our dependency to the collection. It’s time to inject where we need it, i.e., in the Web API. Our GET method is inside the CategoryController, and we want an instance of categoryrepository. So, let’s create a CategoryController constructor which expects the ICategoryRepository type. From this parameterized constructor, set the private property of type ICategoryRepository, which will be used to call GetCategories from the GET method.

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace DIinCore.Controllers
{
    [Route("api/Category")]
    public class CategoryController : Controller
    {
        private ICategoryRepository categoryRepository { get; set; }

        public CategoryController(ICategoryRepository categoryRepository)
        {
            this.categoryRepository = categoryRepository;
        }

        [HttpGet]
        public async Task<IActionResult> Get()
        {
            List<Category> categories = categoryRepository.GetCategories();
            return Ok(categories);
        }
    }
}

Run the application, and we will be able to see the result of the GET method of CategoryController. Now, even though we haven’t created an instance of CategoryRepository, which is expected by CategoryController, we are able to call the GET method successfully. The instance of CategoryRepository has been resolved dynamically, i.e., our Dependency Injection.

Dependency Injection In .NET Core 

You can download the sample code from the top of this article.

Advantages of using dependency injection in .NET Core

Here are some of the advantages of using dependency injection in .NET Core:

  • Loose coupling: Dependency injection allows us to decouple our classes from their dependencies. This makes our code more maintainable and easier to test.
  • Testability: Dependency injection makes our code more testable because we can mock out dependencies in our unit tests.
  • Extensibility: Dependency injection makes our code more extensible because we can easily swap out dependencies.
  • Reusability: Dependency injection makes our code more reusable because we can easily share dependencies between different classes.

What are the different types of dependency injection?

There are two main types of dependency injection: constructor injection and property injection.

  • Constructor injection: Constructor injection is the most common type of dependency injection. In constructor injection, we inject the dependency into the class constructor.
  • Property injection: Property injection is less common than constructor injection. In property injection, we inject the dependency into a property of the class.

Next: .NET Core Dependency Injection - One Interface, Multiple Implementations