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