Introduction
Dependency Injection (DI) is one of the most important concepts in modern .NET development. In .NET 8, DI is built-in and widely used in ASP.NET Core applications to create clean, maintainable, and testable code.
In simple terms, Dependency Injection means providing the required dependencies (services, objects, or classes) to a class rather than creating them within the class.
This helps reduce tight coupling, improves code flexibility, and makes applications easier to scale and test.
In this article, we will understand how Dependency Injection works in .NET 8, its types, and how to implement it step by step with practical examples.
What is Dependency Injection in .NET 8?
Dependency Injection is a design pattern used to achieve Inversion of Control (IoC).
Instead of a class creating its own dependencies, those dependencies are injected from outside.
Without Dependency Injection (Tightly Coupled Code)
public class EmailService
{
public void SendEmail(string message)
{
Console.WriteLine("Email sent: " + message);
}
}
public class Notification
{
private EmailService _emailService = new EmailService();
public void Notify()
{
_emailService.SendEmail("Hello User");
}
}
Problem
With Dependency Injection (Loosely Coupled Code)
public interface IEmailService
{
void SendEmail(string message);
}
public class EmailService : IEmailService
{
public void SendEmail(string message)
{
Console.WriteLine("Email sent: " + message);
}
}
public class Notification
{
private readonly IEmailService _emailService;
public Notification(IEmailService emailService)
{
_emailService = emailService;
}
public void Notify()
{
_emailService.SendEmail("Hello User");
}
}
Now the dependency is injected, not created inside the class.
Types of Dependency Injection in .NET 8
1. Constructor Injection (Most Common)
Dependencies are provided through the constructor.
public class UserService
{
private readonly IEmailService _emailService;
public UserService(IEmailService emailService)
{
_emailService = emailService;
}
}
2. Method Injection
Dependencies are passed as method parameters.
public void Process(IEmailService emailService)
{
emailService.SendEmail("Processing...");
}
3. Property Injection (Less Common)
Dependencies are set via properties.
public IEmailService EmailService { get; set; }
Built-in Dependency Injection in .NET 8
.NET 8 provides a built-in IoC container using IServiceCollection.
You typically configure services in Program.cs.
Step-by-Step: Implement Dependency Injection in .NET 8
Step 1: Create Interface
public interface IMessageService
{
string GetMessage();
}
Step 2: Create Implementation
public class MessageService : IMessageService
{
public string GetMessage()
{
return "Hello from Dependency Injection";
}
}
Step 3: Register Service in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IMessageService, MessageService>();
var app = builder.Build();
Step 4: Use Service in Controller
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
private readonly IMessageService _messageService;
public HomeController(IMessageService messageService)
{
_messageService = messageService;
}
[HttpGet]
public string Get()
{
return _messageService.GetMessage();
}
}
Now the service is injected automatically by .NET.
Service Lifetimes in .NET 8
Understanding service lifetimes is very important for performance and behavior.
1. Transient
builder.Services.AddTransient<IMessageService, MessageService>();
2. Scoped
builder.Services.AddScoped<IMessageService, MessageService>();
3. Singleton
builder.Services.AddSingleton<IMessageService, MessageService>();
Real-World Example (ASP.NET Core)
Imagine you are building a user management system.
Repository Layer
public interface IUserRepository
{
string GetUser();
}
public class UserRepository : IUserRepository
{
public string GetUser()
{
return "User Data";
}
}
Service Layer
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
public string GetUserDetails()
{
return _repository.GetUser();
}
}
Register Services
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<UserService>();
Controller
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userService)
{
_userService = userService;
}
[HttpGet]
public string GetUser()
{
return _userService.GetUserDetails();
}
}
Common Mistakes in Dependency Injection
Using Singleton for services that depend on Scoped services
Not using interfaces
Creating objects manually instead of using DI
Registering services incorrectly
Best Practices for Dependency Injection in .NET 8
Always use interfaces for abstraction
Prefer constructor injection
Use Scoped lifetime for database-related services
Keep services small and focused
Avoid service locator pattern
Why Dependency Injection is Important
Improves code maintainability
Makes unit testing easier
Reduces tight coupling
Supports scalable architecture
Summary
Dependency Injection in .NET 8 is a powerful feature that helps developers build clean, flexible, and maintainable applications. By injecting dependencies instead of creating them manually, you can improve performance, testability, and scalability.
Whether you are building ASP.NET Core APIs or enterprise-level applications, understanding and using Dependency Injection correctly is essential for writing high-quality C# code.