Files
Desktop2.0/e-book/Wbudowane wstrzykiwanie zależności (DI) w architekturze Generic Host.md

106 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Wstęp merytoryczny: Wbudowane wstrzykiwanie zależności pod kontrolą Hosta
Przejście z architektury **.NET Framework 4.8** na nowoczesny ekosystem **.NET 10** wymusza zmianę sposobu zarządzania cyklem życia obiektów i ich zależnościami. W klasycznych aplikacjach desktopowych (WPF, WinForms) wstrzykiwanie zależności (Dependency Injection, DI) było mechanizmem opcjonalnym, często dodawanym za pomocą zewnętrznych bibliotek (np. Autofac, Ninject). W architekturze ASP.NET Core wbudowane wstrzykiwanie zależności stanowi nierozerwalny fundament platformy.
Centralnym punktem zarządzającym wstrzykiwaniem zależności jest **Generic Host** (Host Ogólny), najczęściej powoływany za pomocą instancji `WebApplicationBuilder`. Host hermetyzuje wszystkie zasoby aplikacji: serwer HTTP, konfigurację, potok oprogramowania pośredniczącego (Middleware) oraz centralny kontener DI. Oznacza to, że w środowisku webowym to nie system operacyjny Windows ani kod interfejsu użytkownika zarządza pamięcią obiektów, lecz wysoce zoptymalizowana infrastruktura Hosta.
## Analiza Porównawcza: Zarządzanie zależnościami (Desktop vs Web)
| **Koncepcja Desktop (.NET 4.8)** | **Odpowiednik w .NET 10 (Generic Host)** | **Charakter zmiany architektonicznej** |
| --- | --- | --- |
| **Ręczne powoływanie obiektów (`new`)** | **Kontener DI (`builder.Services`)** | Odrzucenie ścisłego powiązania (tight coupling). Aplikacja polega na odwróceniu kontroli (IoC), a Host wstrzykuje instancje do konstruktorów. |
| **Zmienne statyczne / Wzorzec Singleton** | **Rejestracja o cyklu życia (Singleton/Scoped)** | Eliminacja globalnego stanu aplikacji (Stateful) na rzecz zarządzania bytem obiektów w obrębie cyklu życia bezstanowego żądania HTTP (Stateless). |
| **Zewnętrzne biblioteki DI** | **Wbudowane `Microsoft.Extensions.DependencyInjection`** | Standaryzacja. Mechanizm DI jest zintegrowany bezpośrednio wewnątrz Hosta i dostarczany natywnie przez framework. |
| **Ścisłe zależności między warstwami** | **Wstrzykiwanie przez konstruktor lub `@inject`** | Logika biznesowa jest przekazywana do nowoczesnych kontrolerów lub komponentów Blazor wyłącznie przez mechanizmy wbudowane w platformę. |
### Głębokie Nurkowanie (Deep Dive): Anatomia DI w Generic Host
W modelu ASP.NET Core proces inicjalizacji i orkiestracji działania aplikacji opiera się na dwóch fazach budowy Hosta. W fazie początkowej obiekt `WebApplicationBuilder` udostępnia właściwość `Services` reprezentującą kolekcję usług (`IServiceCollection`). To na tym etapie infrastruktura dodaje logikę dostępu do bazy danych, logowania, autoryzacji oraz renderowania komponentów interfejsu.
Kiedy programista wywołuje metodę `builder.Build()`, Host zatrzaskuje konfigurację, generuje docelowy kontener DI (`IServiceProvider`) i konfiguruje środowisko uruchomieniowe wraz z wieloplatformowym serwerem HTTP Kestrel. Od tego momentu każda składowa potoku Middleware, każdy kontroler oraz każdy komponent Blazor otrzymuje swoje zależności wyłącznie w sposób automatyczny na etapie wykonania. Model ten eliminuje narzut związany z manualnym przekazywaniem parametrów systemowych w głąb drzewa wywołań.
## Dobre Praktyki i Antywzorce
* **Antywzorzec: Ręczna rezolucja usług:** Pobieranie instancji poprzez anty-wzorzec *Service Locator* (np. bezpośrednie odpytywanie `ServiceProvider` w warstwie logiki) łamie spójność kontroli sprawowanej przez Hosta i ogranicza przejrzystość.
* **Dobra Praktyka: Wstrzykiwanie przez konstruktor (Constructor Injection):** Zależności powinny być zawsze deklarowane jawnie jako parametry konstruktora (lub poprzez dyrektywę `@inject` w plikach `.razor`). Gwarantuje to testowalność i przejrzystość wymogów danej klasy, co ułatwia zarządzanie dostarczanymi zależnościami.
## Laboratorium kodu: Rejestracja usług i ich użycie w architekturze Hosta
Poniższy przykład demonstruje ewolucję inicjalizacji na poziomie punktu wejścia aplikacji. Użyto w nim wzorca Minimal API, gdzie `Generic Host` ładuje mechanizmy infrastruktury z jednego punktu, pozwalając na precyzyjne odizolowanie bazy danych od interfejsu UI.
```csharp
// Program.cs w .NET 10 (Konfiguracja Hosta)
using Microsoft.EntityFrameworkCore;
using ModernApp.Data;
// 1. Inicjalizacja Hosta i infrastruktury
var builder = WebApplication.CreateBuilder(args);
// 2. Rejestracja wbudowanego mechanizmu wstrzykiwania zależności (DI)
// Usługi środowiskowe (np. konfiguracja JSON) są dostępne od razu z poziomu Hosta
builder.Services.AddDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Brak konfiguracji bazy danych."))); //
// Rejestracja frameworka komponentów i obsługi cyklu życia UI
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(); //
// 3. Zbudowanie Hosta zatrzaśnięcie kontenera DI
var app = builder.Build(); //
// (Konfiguracja bezstanowego potoku Middleware...)
app.UseHttpsRedirection(); //
app.UseAntiforgery(); //
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode(); //
// 4. Start Hosta i serwera Kestrel
app.Run(); //
```
Zastosowanie wstrzykniętych zasobów w asynchronicznym środowisku webowym za pomocą dedykowanej dyrektywy frameworka:
```html
@* TasksList.razor *@
@page "/tasks"
@using ModernApp.Data
@using Microsoft.EntityFrameworkCore
@* Deklaratywne żądanie wstrzyknięcia usługi utrzymywanej przez Generic Host *@
@inject IDbContextFactory<AppDbContext> DbFactory
<div class="task-board">
@if (_tasks == null)
{
<p><em>Pobieranie danych ze strumienia DI...</em></p>
}
else
{
<table class="table">
@foreach (var task in _tasks)
{
<tr>
<td>@task.Title</td>
</tr>
}
</table>
}
</div>
@code {
private List<TaskDto>? _tasks;
// Metoda cyklu życia, w której asynchronicznie zużywamy zasoby dostarczone z Hosta
protected override async Task OnInitializedAsync()
{
await using var context = DbFactory.CreateDbContext();
_tasks = await context.Tasks.ToListAsync();
}
}
```
## Wnioski architektoniczne
Przesunięcie zarządzania bytem i instancjonowaniem obiektów z poziomu kodu sterowanego zdarzeniami w architekturze Desktop (np. powiązanej z cyklem życia kontrolki w WPF), bezpośrednio do wbudowanego, centralnego mechanizmu kontenera DI zintegrowanego z modelem **Generic Host**, pozwala na radykalne podniesienie testowalności i skalowalności. Architektura w .NET 10 zapobiega wyciekom pamięci dzięki standaryzowanym cyklom życia obiektów (Transient, Scoped, Singleton) zarządzanym i powiązanym z ramami czasowymi żądań HTTP przetwarzanych przez Middleware. Umożliwia to zwinne budowanie i wdrażanie odseparowanej, ustandaryzowanej logiki aplikacji.