ASP.NET Core  

Dependency Injection in ASP.NET Core for Total Beginners

Introduction

When you are building applications in ASP.NET Core, you will quickly encounter the term Dependency Injection (DI).

But what exactly is dependency injection, and why is it so important?

In simple words, Dependency Injection is a technique to give an object the things it needs (its dependencies) instead of the object creating them itself.

This may sound technical, but it helps in making your code cleaner, more modular, and easier to test.

This article explains dependency injection in ASP.NET Core from scratch, with simple examples and easy-to-understand explanations for beginners.

What is Dependency Injection?

Every object in an application often depends on other objects to work properly. These are called dependencies.

For example:

public class UserService
{
    private readonly UserRepository _userRepository = new UserRepository();

    public IEnumerable<User> GetUsers()
    {
        return _userRepository.GetAllUsers();
    }
}

In this code, UserService creates its own UserRepository.

Problem

  • If UserRepository changes, you must update UserService.

  • Testing UserService becomes harder because it always uses a real UserRepository.

Solution: Instead of creating UserRepository inside UserService, we inject it from outside.

public class UserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public IEnumerable<User> GetUsers()
    {
        return _userRepository.GetAllUsers();
    }
}

Here, UserRepository is injected into UserService.

This is Dependency Injection.

Why Use Dependency Injection?

  1. Loose Coupling: Classes don’t create their dependencies, so they are less dependent on specific implementations.

  2. Easier Testing: You can inject fake or mock dependencies during unit tests.

  3. Better Maintainability: Change the dependency in one place, and it affects all classes that use it.

  4. Centralized Configuration: Manage all dependencies in a central place (Startup or Program class).

How Dependency Injection Works in ASP.NET Core

ASP.NET Core has built-in support for dependency injection. It uses a Service Container (also called IoC container) to manage dependencies.

Step 1: Register Services

You register classes and interfaces in the Program.cs file.

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();
  • AddScoped – One instance per HTTP request.

  • AddSingleton – Single instance for the entire application lifetime.

  • AddTransient – New instance every time it is requested.

Step 2: Inject Dependencies into Controllers

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet]
    public IEnumerable<User> GetUsers()
    {
        return _userService.GetUsers();
    }
}
  • ASP.NET Core automatically provides the UserService instance to the controller.

  • You don’t need to create it manually.

Types of Dependency Injection in ASP.NET Core

  1. Constructor Injection: Most common. Dependencies are passed via constructor.

  2. Property Injection: Dependencies are set via properties (less common).

  3. Method Injection: Dependencies are passed directly to methods (rarely used).

Example of Constructor Injection (Recommended)

public class OrderService
{
    private readonly IOrderRepository _orderRepository;

    public OrderService(IOrderRepository orderRepository)
    {
        _orderRepository = orderRepository;
    }

    public IEnumerable<Order> GetOrders()
    {
        return _orderRepository.GetAllOrders();
    }
}

Example: Dependency Injection with ASP.NET Core

Let’s create a small example with Users:

1. Create Model

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

2. Create Repository Interface

public interface IUserRepository
{
    IEnumerable<User> GetAllUsers();
}

3. Create Repository Implementation

public class UserRepository : IUserRepository
{
    public IEnumerable<User> GetAllUsers()
    {
        return new List<User>
        {
            new User { Id = 1, Name = "Rahul" },
            new User { Id = 2, Name = "Priya" }
        };
    }
}

4. Create Service

public interface IUserService
{
    IEnumerable<User> GetUsers();
}

public class UserService : IUserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public IEnumerable<User> GetUsers()
    {
        return _userRepository.GetAllUsers();
    }
}

5. Register Services in Program.cs

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserService, UserService>();

6. Inject Service in Controller

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet]
    public IEnumerable<User> GetUsers()
    {
        return _userService.GetUsers();
    }
}

How It Works

  1. Controller requests IUserService.

  2. ASP.NET Core looks in the Service Container and finds UserService.

  3. UserService needs IUserRepository. The container provides UserRepository.

  4. Everything is automatically injected, and the controller works without manually creating objects.

Lifetime of Services

  1. Transient: New instance every time. AddTransient<TInterface, TImplementation>()

  2. Scoped: One instance per HTTP request. AddScoped<TInterface, TImplementation>()

  3. Singleton: Single instance for the whole application. AddSingleton<TInterface, TImplementation>()

Example

builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();

Benefits of DI in ASP.NET Core

  1. Cleaner and more organized code.

  2. Reduced coupling between classes.

  3. Easier unit testing using mocks.

  4. Centralized configuration of services.

  5. Makes code flexible and scalable for future changes.

Common Mistakes to Avoid

  1. Not using interfaces: Avoid injecting concrete classes directly.

  2. Mixing lifetimes incorrectly: e.g., injecting a transient service into a singleton.

  3. Creating dependencies manually in controllers: defeats the purpose of DI.

  4. Registering services multiple times unnecessarily: may lead to unexpected behavior.

Conclusion

Dependency Injection is a powerful feature in ASP.NET Core that helps you write clean, testable, and maintainable applications.

By using DI

  • You no longer manually create objects in controllers.

  • Dependencies are managed automatically by the framework.

  • You can easily swap implementations for testing or future updates.

For beginners, mastering DI is essential before moving to advanced topics like Repository Pattern, Unit of Work, and Middleware in ASP.NET Core.