vault backup: 2026-05-14 19:46:36
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
## 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.
|
||||
Reference in New Issue
Block a user