🧩 Deep Dive: Dependency Injection is the backbone of ASP.NET Core. Master lifetimes, registration, and advanced scenarios.
DI is everywhere in ASP.NET Core. Understanding it is essential.
🚀 Service Lifetimes
// ✅ 1. Transient - Created every time
builder.Services.AddTransient<ITransientService, TransientService>();
// ✅ 2. Scoped - Created per request/scope
builder.Services.AddScoped<IScopedService, ScopedService>();
// ✅ 3. Singleton - Created once
builder.Services.AddSingleton<ISingletonService, SingletonService>();
// ✅ 4. Factory registration
builder.Services.AddScoped<IService>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
return new Service(config["ServiceUrl"]);
});
// ✅ 5. Register with implementation
builder.Services.AddScoped<MyService>();
// ✅ 6. Register multiple implementations
builder.Services.AddScoped<INotificationService, EmailNotification>();
builder.Services.AddScoped<INotificationService, SmsNotification>();
💡 Real-World: Advanced DI
// ✅ Decorator pattern with DI
public interface IOrderService
{
Task CreateOrderAsync(Order order);
}
public class OrderService : IOrderService
{
public async Task CreateOrderAsync(Order order)
{
// Core logic
}
}
public class LoggingOrderServiceDecorator : IOrderService
{
private readonly IOrderService _inner;
private readonly ILogger _logger;
public LoggingOrderServiceDecorator(IOrderService inner, ILogger<LoggingOrderServiceDecorator> logger)
{
_inner = inner;
_logger = logger;
}
public async Task CreateOrderAsync(Order order)
{
_logger.LogInformation("Creating order {OrderId}", order.Id);
await _inner.CreateOrderAsync(order);
_logger.LogInformation("Order {OrderId} created", order.Id);
}
}
// ✅ Register decorator
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderServiceDecorator>();
// ✅ Lazy resolution
public class MyService
{
private readonly Lazy<IHeavyService> _heavyService;
public MyService(Lazy<IHeavyService> heavyService)
{
_heavyService = heavyService;
}
public void DoWork()
{
// ✅ Service is created only when accessed
_heavyService.Value.Process();
}
}
// ✅ Conditional resolution
public class ServiceFactory
{
private readonly IServiceProvider _provider;
public ServiceFactory(IServiceProvider provider)
{
_provider = provider;
}
public INotificationService GetService(string type)
{
return type switch
{
"Email" => _provider.GetRequiredService<EmailNotification>(),
"SMS" => _provider.GetRequiredService<SmsNotification>(),
_ => _provider.GetRequiredService<DefaultNotification>()
};
}
}
💡 Expert Tip: Scope Management
// ✅ Create custom scope
public class BackgroundService
{
private readonly IServiceProvider _provider;
public BackgroundService(IServiceProvider provider)
{
_provider = provider;
}
public async Task ExecuteAsync()
{
// ✅ Create a new scope
using var scope = _provider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<BackgroundService>>();
try
{
await dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Error in background task");
}
}
}
// ✅ Factory pattern with DI
public interface IServiceFactory
{
T CreateScopedService<T>() where T : class;
}
public class ServiceFactory : IServiceFactory
{
private readonly IServiceProvider _provider;
public ServiceFactory(IServiceProvider provider)
{
_provider = provider;
}
public T CreateScopedService<T>() where T : class
{
using var scope = _provider.CreateScope();
return scope.ServiceProvider.GetRequiredService<T>();
}
}
