Il contenitore di dependency injection integrato in .NET copre la grande maggioranza dei casi. Conoscerne bene i tempi di vita e le funzioni più recenti evita bug difficili da trovare.
I tre lifetime
- Singleton: un'unica istanza per tutta l'applicazione. Deve essere thread-safe.
- Scoped: un'istanza per richiesta web (o per scope creato a mano).
- Transient: una nuova istanza ogni volta che viene richiesta.
L'errore classico è iniettare un servizio scoped in un singleton: il singleton lo trattiene per sempre, condividendo per esempio un contesto di database tra richieste diverse. In ambiente di sviluppo .NET lo segnala all'avvio.
Keyed services
Quando servono più implementazioni della stessa interfaccia, si registrano con una chiave:
builder.Services.AddKeyedSingleton<INotificatore, NotificatoreEmail>("email");
builder.Services.AddKeyedSingleton<INotificatore, NotificatoreSms>("sms");
public class AvvisiService(
[FromKeyedServices("email")] INotificatore email,
[FromKeyedServices("sms")] INotificatore sms)
{
public Task UrgenteAsync(string testo) =>
Task.WhenAll(email.InviaAsync(testo), sms.InviaAsync(testo));
}
Nelle Minimal API lo stesso attributo si usa sui parametri dell'handler.
Servizi in background e scope
Un BackgroundService è un singleton. Per usare servizi scoped, come un repository, si crea uno scope esplicito:
public class PuliziaService(IServiceScopeFactory scopes) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
while (await timer.WaitForNextTickAsync(ct))
{
using var scope = scopes.CreateScope();
var repo = scope.ServiceProvider.GetRequiredService<ILogRepository>();
await repo.EliminaVecchiAsync(ct);
}
}
}
Regola pratica: registra come singleton solo ciò che è davvero condivisibile e senza stato per utente, e lascia scoped tutto ciò che tocca dati della richiesta.
Commenti (0)
Nessun commento ancora.