Durées de vie des services : Transient, Scoped, Singleton
Découvrez comment les durées de vie Transient, Scoped et Singleton influencent la création, le partage et la libération des objets.
Durées de vie des services : Transient, Scoped, Singleton est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
Pourquoi les durées de vie sont importantes
La durée de vie d’un service détermine combien de temps une instance existe et combien d’instances sont créées. Un mauvais choix peut provoquer des erreurs difficiles à repérer, comme des fuites de données entre les requêtes ou une création excessive d’objets.
Transitoire : une nouvelle instance à chaque fois
Les services transitoires sont créés à neuf chaque fois qu’ils sont demandés au conteneur. Utilisez-les pour les services légers et sans état, lorsque le partage d’un état pourrait être dangereux.
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Each resolution creates a new instance:
// var a = sp.GetRequiredService<IEmailSender>(); // new
// var b = sp.GetRequiredService<IEmailSender>(); // new (different)À portée limitée : une fois par requête
Les services à portée limitée sont créés une fois par portée, généralement pour une requête HTTP dans ASP.NET Core. La même instance est partagée pendant une requête, puis une nouvelle instance est créée pour la requête suivante.
builder.Services.AddScoped<AppDbContext>();
// Within a single HTTP request:
// Both resolve to the SAME AppDbContext instance
// -> consistent unit of work across the requestInstance unique : une seule pour la durée de vie de l’application
Les services à instance unique sont créés une fois, puis réutilisés pendant toute la durée de vie de l’application. Utilisez-les pour les services coûteux à créer et sûrs pour une utilisation concurrente, comme les caches de configuration.
builder.Services.AddSingleton<IMemoryCache, MemoryCache>();
// Or register a pre-built instance:
var config = new AppConfig { MaxRetries = 3 };
builder.Services.AddSingleton<IAppConfig>(config);Comparer les trois durées de vie
Voici une comparaison rapide côte à côte :
- Transitoire — une nouvelle instance par requête ; utilitaires sans état
- À portée limitée — une instance par requête HTTP ; DbContext, unité de travail
- À instance unique — une instance pour toute la durée de vie de l’application ; caches, configurations
Le problème des dépendances captives
Injecter un service à portée limitée ou transitoire dans un service à instance unique est une erreur courante. Le service à instance unique conserve une référence vers le service à durée de vie courte, ce qui le maintient en vie trop longtemps.
// BAD: Singleton captures a Scoped service
public class MySingleton
{
private readonly AppDbContext _db; // Scoped!
public MySingleton(AppDbContext db) => _db = db;
// _db is now stuck alive for the app's lifetime
}Détecter les violations de portée
Activez ValidateScopes afin que le conteneur lève une exception au démarrage lorsqu’une dépendance captive est détectée. Cela permet de repérer les erreurs avant qu’elles ne provoquent des problèmes de données en production.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true; // detect captive deps
options.ValidateOnBuild = true; // fail fast at start
});Services à portée limitée en dehors d’une requête
Pour résoudre des services à portée limitée dans une tâche en arrière-plan, créez une portée explicite à l’aide de IServiceScopeFactory. Ne résolvez jamais directement des services à portée limitée depuis le conteneur racine.
public class MyWorker : BackgroundService
{
private readonly IServiceScopeFactory _factory;
public MyWorker(IServiceScopeFactory factory) => _factory = factory;
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var scope = _factory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Users.ToListAsync(ct);
}
}Services transitoires libérables
Les services IDisposable transitoires résolus depuis le conteneur racine sont suivis jusqu’à la libération du conteneur, lors de l’arrêt de l’application. Résolvez-les dans une portée afin de libérer rapidement les ressources.
// Correctly scoped transient disposable
using var scope = sp.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<MyDisposableService>();
await svc.DoWorkAsync();
// svc.Dispose() called when scope is disposedChoisir la bonne durée de vie
Voici un guide de décision simple :
- Le service est-il sans état ? → Transitoire
- Dépend-il d’une requête, par exemple de l’identité de l’utilisateur ou de DbContext ? → À portée limitée
- Est-il sûr pour une utilisation concurrente et coûteux à construire ? → À instance unique
Cas concret : les trois durées de vie ensemble
Une API web classique enregistre différents services avec des durées de vie adaptées afin d’assurer la fiabilité et les performances.
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddScoped<AppDbContext>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<OrderService>();
builder.Services.AddTransient<IEmailSender, SendGridEmailSender>();Vérification rapide
Quel problème survient lorsqu’un service à instance unique conserve une référence vers un service à portée limitée ?
Récapitulatif : durées de vie des services
Points essentiels :
- Transitoire : une nouvelle instance à chaque fois — adapté aux services légers et sans état
- À portée limitée : une instance par requête — DbContext, dépôts, unité de travail
- À instance unique : une seule instance pour toujours — caches, configurations, utilitaires sûrs pour une utilisation concurrente
- N’injectez jamais un service à courte durée de vie dans un service à durée de vie plus longue, car cela crée une dépendance captive
- Utilisez
IServiceScopeFactorypour résoudre des services à portée limitée dans les tâches en arrière-plan
Questions Fréquemment Posées
La leçon « Durées de vie des services : Transient, Scoped, Singleton » est-elle gratuite ?
Oui — le texte complet de « Durées de vie des services : Transient, Scoped, Singleton » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Durées de vie des services : Transient, Scoped, Singleton » ?
Découvrez comment les durées de vie Transient, Scoped et Singleton influencent la création, le partage et la libération des objets. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Durées de vie des services : Transient, Scoped, Singleton » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Fondamentaux des conteneurs DI
- Durées de vie des services : Transient, Scoped, Singleton
- Injection par constructeur et interfaces
- Patrons Factory et Options