Dependency Injection (DI) is one of the most important concepts in ASP.NET Core. It allows your application to provide the objects and services that a class needs instead of forcing the class to create those objects itself.

If you are new to ASP.NET Core, Dependency Injection may sound complicated at first. In reality, the basic idea is quite simple:

Instead of a class creating its own dependencies, ASP.NET Core provides those dependencies to the class.

ASP.NET Core has built-in support for Dependency Injection, so you can register services and inject them into controllers, Razor Pages, Blazor components, middleware, and other application components.

๐Ÿ“Œ What Is Dependency Injection?

A dependency is an object that another class needs to perform its work.

For example, imagine a controller that needs a service to retrieve customer information.

public class CustomerController
{
    private CustomerService _customerService;

    public CustomerController()
    {
        _customerService = new CustomerService();
    }
}

This approach works, but the controller is responsible for creating its own CustomerService.

That creates a strong dependency between the controller and the concrete service implementation.

With Dependency Injection, the service can instead be supplied to the controller.

public class CustomerController
{
    private readonly CustomerService _customerService;

    public CustomerController(CustomerService customerService)
    {
        _customerService = customerService;
    }
}

The controller no longer needs to create the service itself.

๐ŸŽฏ Why Is Dependency Injection Useful?

Dependency Injection provides several important benefits when building ASP.NET Core applications.

  • Reduces tight coupling between classes.
  • Makes code easier to test.
  • Makes services easier to replace.
  • Centralizes service configuration.
  • Allows ASP.NET Core to manage service lifetimes.
  • Improves the overall maintainability of an application.

๐Ÿ—๏ธ Dependency Injection in ASP.NET Core

ASP.NET Core includes a built-in Dependency Injection container.

Services are registered with the application's service collection and can then be requested by application components.

A simple example in Program.cs looks like this:

builder.Services.AddScoped<CustomerService>();

Once the service is registered, ASP.NET Core can provide it when a class requests it.

โš™๏ธ A Simple Complete Example

Let's create a small service that returns a message.

Step 1: Create the Service

public class MessageService
{
    public string GetMessage()
    {
        return "Hello from the MessageService!";
    }
}

Step 2: Register the Service

Open Program.cs and register the service:

builder.Services.AddScoped<MessageService>();

Step 3: Inject the Service

The service can now be injected into a controller.

public class HomeController : Controller
{
    private readonly MessageService _messageService;

    public HomeController(MessageService messageService)
    {
        _messageService = messageService;
    }

    public IActionResult Index()
    {
        var message = _messageService.GetMessage();

        return Content(message);
    }
}

ASP.NET Core creates the MessageService and supplies it to the controller's constructor.

๐Ÿงฉ Constructor Injection

Constructor Injection is one of the most common forms of Dependency Injection in ASP.NET Core.

The dependency is declared as a constructor parameter.

public class ProductController : Controller
{
    private readonly IProductService _productService;

    public ProductController(IProductService productService)
    {
        _productService = productService;
    }
}

ASP.NET Core examines the constructor and resolves the requested dependency from the Dependency Injection container.

We will explore Constructor Injection in more detail in a dedicated supporting article in this series.

๐Ÿ”Œ Using Interfaces with Dependency Injection

Dependency Injection becomes especially useful when you program against interfaces rather than concrete implementations.

For example:

public interface IMessageService
{
    string GetMessage();
}

An implementation can then be created:

public class MessageService : IMessageService
{
    public string GetMessage()
    {
        return "Hello from Dependency Injection!";
    }
}

Register the implementation:

builder.Services.AddScoped<IMessageService, MessageService>();

The controller can depend on the interface:

public class HomeController : Controller
{
    private readonly IMessageService _messageService;

    public HomeController(IMessageService messageService)
    {
        _messageService = messageService;
    }
}

This makes it easier to replace the implementation later and makes unit testing easier because a test can provide a different implementation of the interface.

โฑ๏ธ Service Lifetimes in ASP.NET Core

ASP.NET Core provides three commonly used service lifetimes:

  • Transient โ€” a new instance is created each time the service is requested.
  • Scoped โ€” one instance is generally created for each scope.
  • Singleton โ€” one instance is used for the lifetime of the application.

For example:

builder.Services.AddTransient<IEmailService, EmailService>();

builder.Services.AddScoped<IOrderService, OrderService>();

builder.Services.AddSingleton<IApplicationService, ApplicationService>();
๐Ÿ’ก Tip: Choosing the correct service lifetime is important. A service that holds state or depends on another scoped service should not automatically be registered as a singleton.

For a detailed comparison, see:

๐Ÿ”„ Singleton vs Scoped vs Transient in ASP.NET Core: What's the Difference?

๐Ÿงช Dependency Injection and Unit Testing

One of the major advantages of Dependency Injection is that it makes classes easier to test.

Suppose a controller depends on:

IProductService

A unit test can provide a test implementation instead of connecting to a real database or external service.

This separation makes tests easier to write and maintain.

๐Ÿšจ What Happens If a Service Is Not Registered?

If ASP.NET Core tries to resolve a service that hasn't been registered, the application can throw a dependency resolution exception.

For example, if a controller requests:

public HomeController(IMessageService messageService)
{
    _messageService = messageService;
}

but IMessageService hasn't been registered, ASP.NET Core cannot resolve the dependency.

You would normally register it in Program.cs:

builder.Services.AddScoped<IMessageService, MessageService>();

๐Ÿ“š Dependency Injection in Blazor

Dependency Injection is not limited to MVC controllers. ASP.NET Core's Dependency Injection system is also heavily used by Blazor applications.

A Blazor component can inject a service using the @inject directive.

@inject IMessageService MessageService

<h3>@MessageService.GetMessage()</h3>

This allows Blazor components to consume services without manually creating those service objects.

๐Ÿง  Dependency Injection vs Creating Objects Manually

Manual Creation Dependency Injection
Class creates the dependency Dependency is supplied to the class
Often tightly coupled Encourages loose coupling
Harder to replace implementations Implementations can be changed through registration
Testing can be more difficult Easier to substitute dependencies during testing

๐Ÿ“– Related Dependency Injection Articles

๐Ÿ›๏ธ ASP.NET Core Dependency Injection Explained with Examples (Beginner to Advanced)

โš™๏ธ How to Register Services in ASP.NET Core Dependency Injection

๐Ÿ”„ Singleton vs Scoped vs Transient in ASP.NET Core: What's the Difference?

๐Ÿงฉ How to Use Constructor Injection in ASP.NET Core

๐Ÿ’‰ How to Inject Services into Controllers, Razor Pages, and Blazor

๐Ÿšจ Common Dependency Injection Errors in ASP.NET Core and How to Fix Them

๐Ÿš€ Advanced Dependency Injection in ASP.NET Core: Factory, Keyed Services, and Service Lifetimes

๐ŸŽฏ Conclusion

Dependency Injection is a fundamental part of ASP.NET Core development. Instead of creating dependencies directly inside your classes, you can register services with the built-in Dependency Injection container and allow ASP.NET Core to provide them when needed.

Once you understand service registration, constructor injection, and service lifetimes, you have the foundation needed to build more maintainable and testable ASP.NET Core applications.

๐Ÿš€ What's next?

Learn how to register your own services in ASP.NET Core:

โš™๏ธ How to Register Services in ASP.NET Core Dependency Injection

๐Ÿค– AlgoLassi Assistant Have a question about this tutorial?

Ask AlgoLassi and get an answer plus the tutorials worth studying next.

Ask a question

๐Ÿ’ฌ Comments

Sign in with Google to publish immediately, or comment anonymously and wait for approval.

Comments will appear here when available.