Dependency Injection in ASP.NET Core: Complete Beginner's Guide

Dependency Injection (DI) is one of the most important concepts in modern ASP.NET Core development. It helps developers create applications that are easier to maintain, test, and scale.

ASP.NET Core includes a built-in Dependency Injection container, making it easy to register and consume services throughout your application. Understanding how Dependency Injection works is essential for building professional .NET applications.


Topics Covered
  • What is Dependency Injection?
  • Why use Dependency Injection?
  • Creating services
  • Registering services
  • Constructor Injection
  • Service lifetimes
  • Real-world examples
  • Best practices

What Is Dependency Injection?

Dependency Injection is a design pattern that allows objects to receive their dependencies from an external source rather than creating them directly.

Instead of a class creating its own dependencies, those dependencies are provided to the class when it is instantiated.

This approach reduces coupling between components and improves code maintainability.

Without Dependency Injection

Consider the following example:


        public class ProductController
        {
        private readonly EmailService _emailService;

        public ProductController()
        {
        _emailService = new EmailService();
        }
        }
    

The controller directly creates an EmailService object.

Problems with this approach:

  • Tight coupling between classes.
  • Difficult unit testing.
  • Harder to replace implementations.
  • Reduced maintainability.

With Dependency Injection


        public class ProductController
        {
        private readonly EmailService _emailService;

        public ProductController(
        EmailService emailService)
        {
        _emailService = emailService;
        }
        }
    

The dependency is now supplied externally instead of being created inside the controller.

This makes the code more flexible and easier to test.

Creating a Service

Services contain reusable business logic that can be injected into controllers, other services, and application components.


        public class EmailService
        {
        public void SendEmail(
        string recipient,
        string subject)
        {
        Console.WriteLine(
        $"Email sent to {recipient}");
        }
        }
    

Registering Services

Services are registered inside Program.cs.


        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddScoped<EmailService>();

        var app = builder.Build();
    

Once registered, ASP.NET Core automatically creates and manages service instances.

Using Constructor Injection

Constructor Injection is the most common way to use Dependency Injection.


        public class HomeController : Controller
        {
        private readonly EmailService _emailService;

        public HomeController(
        EmailService emailService)
        {
        _emailService = emailService;
        }

        public IActionResult Index()
        {
        _emailService.SendEmail(
        "user@example.com",
        "Welcome");

        return View();
        }
        }
    

ASP.NET Core automatically injects EmailService when creating the controller.

Using Interfaces

Most production applications register interfaces instead of concrete classes.


        public interface IEmailService
        {
        void SendEmail(
        string recipient,
        string subject);
        }
    

        public class EmailService : IEmailService
        {
        public void SendEmail(
        string recipient,
        string subject)
        {
        Console.WriteLine("Email sent");
        }
        }
    

Register Interface


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

Inject Interface


        public class HomeController : Controller
        {
        private readonly IEmailService _emailService;

        public HomeController(
        IEmailService emailService)
        {
        _emailService = emailService;
        }
        }
    

Using interfaces improves flexibility and testability.

Service Lifetimes

ASP.NET Core supports three primary service lifetimes.

Lifetime Description
Transient New instance every time requested.
Scoped One instance per HTTP request.
Singleton Single instance for application lifetime.

Transient Services


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

A new object is created every time the service is requested.

Good for lightweight and stateless services.

Scoped Services


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

One instance is shared throughout a single request.

This is the most commonly used lifetime in web applications.

Singleton Services


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

A single instance is created and reused throughout the application's lifetime.

Suitable for configuration providers, caching services, and application-wide resources.

Real-World Example

A typical ASP.NET Core application may contain:

  • Repository services
  • Business logic services
  • Email services
  • Payment services
  • Logging services
  • API integration services

        builder.Services.AddScoped<
        IProductRepository,
        ProductRepository>();

        builder.Services.AddScoped<
        IProductService,
        ProductService>();

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

Controllers then consume these services through constructor injection.

Benefits of Dependency Injection

  • Improved maintainability.
  • Better separation of concerns.
  • Easier unit testing.
  • Reduced code duplication.
  • Greater flexibility.
  • Simpler dependency management.

Common Mistakes

  • Registering services with incorrect lifetimes.
  • Injecting too many dependencies into a class.
  • Creating services manually using new.
  • Ignoring interfaces.
  • Using Singleton for request-specific data.

Best Practices

  • Prefer interfaces over concrete classes.
  • Use constructor injection.
  • Choose appropriate service lifetimes.
  • Keep services focused on a single responsibility.
  • Avoid service locator patterns.
  • Organize service registrations clearly.

Frequently Asked Questions

Does ASP.NET Core include Dependency Injection?

Yes. ASP.NET Core has a built-in Dependency Injection container that supports service registration and resolution.

Which service lifetime should I use?

Scoped is typically the best choice for database access and business logic services in web applications.

Why use interfaces?

Interfaces make it easier to swap implementations and create unit tests using mock objects.

Can services depend on other services?

Yes. The Dependency Injection container automatically resolves nested dependencies when creating objects.

Related Tutorials

Conclusion

Dependency Injection is a core feature of ASP.NET Core and a fundamental skill for every .NET developer.

By using services, interfaces, constructor injection, and proper service lifetimes, you can create applications that are easier to maintain, test, and extend as your projects grow.

Once you become comfortable with Dependency Injection, you'll find it much easier to build clean, scalable ASP.NET Core applications.