Czas życia usług: Transient, Scoped, Singleton
Dowiedz się, jak czasy życia Transient, Scoped i Singleton wpływają na tworzenie, współdzielenie i zwalnianie obiektów.
Czas życia usług: Transient, Scoped, Singleton to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.
Dlaczego czasy życia mają znaczenie
Czas życia usługi określa jak długo istnieje instancja oraz ile instancji zostanie utworzonych. Wybór niewłaściwego czasu życia powoduje trudne do wykrycia błędy, takie jak wycieki danych między żądaniami lub nadmierne tworzenie obiektów.
Transient: nowa instancja za każdym razem
Usługi Transient są tworzone od nowa za każdym razem, gdy są pobierane z kontenera. Należy ich używać w przypadku lekkich, bezstanowych usług, dla których współdzielenie stanu mogłoby być niebezpieczne.
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Each resolution creates a new instance:
// var a = sp.GetRequiredService<IEmailSender>(); // new
// var b = sp.GetRequiredService<IEmailSender>(); // new (different)Scoped: raz na żądanie
Usługi Scoped są tworzone raz na każdy zakres — zazwyczaj jest nim jedno żądanie HTTP w ASP.NET Core. Ta sama instancja jest współdzielona w ramach żądania, ale dla kolejnego żądania tworzona jest nowa.
builder.Services.AddScoped<AppDbContext>();
// Within a single HTTP request:
// Both resolve to the SAME AppDbContext instance
// -> consistent unit of work across the requestSingleton: jedna instancja na cały czas działania aplikacji
Usługi Singleton są tworzone raz i ponownie używane przez cały czas działania aplikacji. Należy ich używać w przypadku kosztownych w tworzeniu usług bezpiecznych wątkowo, takich jak pamięci podręczne konfiguracji.
builder.Services.AddSingleton<IMemoryCache, MemoryCache>();
// Or register a pre-built instance:
var config = new AppConfig { MaxRetries = 3 };
builder.Services.AddSingleton<IAppConfig>(config);Porównanie wszystkich trzech czasów życia
Krótkie porównanie:
- Transient — nowa instancja przy każdym żądaniu; bezstanowe narzędzia
- Scoped — jedna instancja na żądanie HTTP; DbContext, Unit of Work
- Singleton — jedna instancja na cały czas działania aplikacji; pamięci podręczne, konfiguracje
Problem uwięzionej zależności
Wstrzyknięcie usługi Scoped lub Transient do usługi Singleton jest częstym błędem. Singleton przechowuje referencję do usługi o krótszym czasie życia, przez co utrzymuje ją przy życiu zbyt długo.
// 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
}Wykrywanie naruszeń zakresu
Należy włączyć ValidateScopes, aby kontener zgłaszał wyjątek podczas uruchamiania, jeśli wykryje uwięzioną zależność. Pozwala to wykryć błędy, zanim spowodują problemy z danymi na środowisku produkcyjnym.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true; // detect captive deps
options.ValidateOnBuild = true; // fail fast at start
});Usługi Scoped poza żądaniem
Aby rozwiązywać usługi Scoped w zadaniu wykonywanym w tle, należy utworzyć jawny zakres za pomocą IServiceScopeFactory. Nigdy nie należy rozwiązywać usług Scoped bezpośrednio z kontenera głównego.
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);
}
}Usługi Transient implementujące IDisposable
Usługi Transient implementujące IDisposable, rozwiązywane z kontenera głównego, są śledzone aż do jego zwolnienia, czyli do zamknięcia aplikacji. Należy rozwiązywać je wewnątrz zakresu, aby niezwłocznie zwalniać zasoby.
// Correctly scoped transient disposable
using var scope = sp.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<MyDisposableService>();
await svc.DoWorkAsync();
// svc.Dispose() called when scope is disposedWybór właściwego czasu życia
Prosty przewodnik decyzyjny:
- Czy usługa jest bezstanowa? → Transient
- Czy zależy od żądania, np. tożsamości użytkownika lub DbContext? → Scoped
- Czy jest bezpieczna wątkowo i kosztowna w tworzeniu? → Singleton
W praktyce: trzy czasy życia razem
Typowe internetowe API rejestruje różne usługi z odpowiednimi czasami życia, aby zapewnić poprawność działania i wydajność.
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>();Szybkie sprawdzenie
Jaki problem występuje, gdy usługa Singleton przechowuje referencję do usługi Scoped?
Podsumowanie: czasy życia usług
Najważniejsze informacje:
- Transient: nowa instancja za każdym razem — odpowiednie dla bezstanowych, lekkich usług
- Scoped: jedna instancja na żądanie — DbContext, repozytoria, Unit of Work
- Singleton: jedna instancja przez cały czas działania aplikacji — pamięci podręczne, konfiguracje, bezpieczne wątkowo narzędzia
- Nigdy nie należy wstrzykiwać usług o krótszym czasie życia do usług o dłuższym czasie życia, ponieważ tworzy to uwięzioną zależność
- Do rozwiązywania usług Scoped w zadaniach wykonywanych w tle należy używać
IServiceScopeFactory
Często zadawane pytania
Czy lekcja „Czas życia usług: Transient, Scoped, Singleton” jest bezpłatna?
Tak — pełny tekst „Czas życia usług: Transient, Scoped, Singleton” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Czas życia usług: Transient, Scoped, Singleton”?
Dowiedz się, jak czasy życia Transient, Scoped i Singleton wpływają na tworzenie, współdzielenie i zwalnianie obiektów. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Czas życia usług: Transient, Scoped, Singleton”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Podstawy kontenera DI
- Czas życia usług: Transient, Scoped, Singleton
- Wstrzykiwanie przez konstruktor i interfejsy
- Wzorce Factory i Options