
Dependency Injection (DI) is built into .NET Core's DNA—let's demystify this powerful pattern that makes your code more testable, maintainable, and flexible!
A design pattern where:
✔ Objects receive their dependencies (services) from outside
✔ No hard-coded dependencies ("new" keyword avoided)
✔ Loose coupling between components
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>(); // New instance every time
services.AddScoped<IDatabaseService, DatabaseService>(); // Per request
services.AddSingleton<ILogger, FileLogger>(); // Single instance forever
}
public class MyController : Controller
{
private readonly IMyService _service;
// Constructor injection (preferred)
public MyController(IMyService service)
{
_service = service;
}
}
✅ Always prefer constructor injection (clearly shows dependencies)
✅ Use interfaces for easy mocking in tests
✅ Avoid Service Locator pattern (anti-pattern!)
// ❌ Avoid this! var service = HttpContext.RequestServices.GetService<IMyService>();
1. Define Interface
public interface IEmailService
{
Task SendEmail(string to, string body);
}
2. Implement Service
public class SmtpEmailService : IEmailService
{
public Task SendEmail(string to, string body)
{
// Actual email sending logic
}
}
3. Register & Use
// Registration
services.AddTransient<IEmailService, SmtpEmailService>();
// Usage in controller
public class UserController : Controller
{
private readonly IEmailService _emailService;
public UserController(IEmailService emailService)
{
_emailService = emailService;
}
}
✔ Easy unit testing (mock dependencies)
✔ Decoupled architecture
✔ Built-in container (no 3rd-party libs needed)
✔ Lifetime management handled automatically
Next Steps: Try replacing a direct dependency in your project with DI today!
#DotNetCore #DependencyInjection #CleanCode #CSharp #BestPractices