0Pricing
C# Academy · Lekcja

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 request

Singleton: 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 disposed

Wybó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

  1. Podstawy kontenera DI
  2. Czas życia usług: Transient, Scoped, Singleton
  3. Wstrzykiwanie przez konstruktor i interfejsy
  4. Wzorce Factory i Options
← Powrót do C# Academy