Dependency Injection: Constructor vs Manual Resolution
Key Considerations
| Category | Constructor Injection | Manual Resolution ( IServiceProvider) | 
|---|---|---|
| Performance | ✅ Optimal (resolve once) | ❌ Overhead per resolution | 
| Memory Safety | ✅ Automatic disposal | ❌ Risk of leaks if usingis omitted | 
| Testability | ✅ Easy (inject mocks) | ❌ Harder (mock IServiceProvider) | 
| Code Clarity | ✅ Explicit dependencies | ❌ Hidden dependencies | 
| Use Case Fit | Default choice for most scenarios | Edge cases (e.g., dynamic/resolved-at-runtime) | 
Example: Constructor Injection (Recommended)
public class EmailService
{
    private readonly ILogger<EmailService> _logger;
    public EmailService(ILogger<EmailService> logger)
    {
        _logger = logger;
    }
    public void SendEmail(string recipient)
    {
        _logger.LogInformation($"Sending email to {recipient}");
    }
}
✅ Good For: - Web API Controllers - Background services - Unit testing with mock dependencies
❌ Not Ideal When: - You need runtime flexibility or conditional resolution of services
Example: Manual Resolution via IServiceProvider
public class DynamicServiceConsumer
{
    private readonly IServiceProvider _provider;
    public DynamicServiceConsumer(IServiceProvider provider)
    {
        _provider = provider;
    }
    public void Execute()
    {
        using var scope = _provider.CreateScope();
        var emailService = scope.ServiceProvider.GetRequiredService<EmailService>();
        emailService.SendEmail("test@example.com");
    }
}
✅ Good For: - Factories - Plugins - Background tasks where service types vary
❌ Not Ideal When: - You want testable, clear, and maintainable code