Files
Desktop2.0/e-book/Architektura ASP.NET Core i Ekosystem Blazor.md

9.3 KiB
Raw Permalink Blame History

Wstęp merytoryczny: Ewolucja od .NET 4.8 do ASP.NET Core i Blazor w .NET 10

Przejście ze środowiska desktopowego, opartego o .NET Framework 4.8 (WinForms, WPF), do nowoczesnego ekosystemu ASP.NET Core i frameworka Blazor reprezentuje fundamentalną zmianę paradygmatu architektonicznego. Ekosystem .NET ewoluował z platformy zamkniętej w systemie Windows (zależnej od ciężkiego API systemu operacyjnego) do wydajnego, wieloplatformowego rozwiązania open-source. ASP.NET Core wprowadza koncepcję zwinnego, modularnego potoku żądań HTTP obsługiwanego przez serwer Kestrel, podczas gdy Blazor pozwala na tworzenie interfejsów użytkownika bezpośrednio w języku C#, eliminując historyczną konieczność dzielenia logiki pomiędzy C# a JavaScript.

Transformacja z tradycyjnego oprogramowania typu "gruby klient" (Stateful) na środowisko webowe w .NET 10 wymaga zrozumienia mechanizmów asynchroniczności, nowoczesnego wstrzykiwania zależności (DI) oraz odmiennego cyklu życia aplikacji.

Analiza Porównawcza: Desktop vs Nowoczesny Web (.NET 10)

Wiedza zdobyta przy tworzeniu aplikacji WPF i WinForms stanowi solidny fundament, jednak wymaga mapowania na wzorce używane w aplikacjach ASP.NET Core i komponentach Blazor:

Koncepcja Desktop (WPF/WinForms) Odpowiednik w .NET 10 (ASP.NET Core / Blazor) Charakter zmiany architektonicznej
Window / UserControl Komponent Razor (.razor) Przejście z XAML/projektanta WinForms na deklaratywny, oparty na C# i HTML komponent UI działający w przeglądarce lub na serwerze.
App.config / Web.config appsettings.json Rezygnacja ze statycznego, ciężkiego pliku XML na rzecz hierarchicznego pliku JSON, z wbudowanym wsparciem wstrzykiwania zależności i konfiguracji środowiskowej.
Zdarzenia UI (Click, Loaded) EventCallback / OnInitializedAsync Zdarzenia systemowe i cykl życia stają się w pełni asynchroniczne i zintegrowane z cyklem renderowania frameworka Blazor.
Globalne instancje (Static/Singleton) Dependency Injection (Wbudowany kontener DI) Stan w ASP.NET Core jest zarządzany poprzez usługi o ściśle określonym cyklu życia (Transient, Scoped, Singleton) rejestrowane w klasie bazowej.
Stan w pamięci RAM aplikacji Obwód (Circuit) / Stateless API Przejście z grubego klienta przechowującego dane lokalnie, na stan rozproszony lub utrzymywany na serwerze za pomocą połączenia SignalR w modelu Blazor Server.

Mechanika potoku ASP.NET Core i Modele Blazor

Hosting i Middleware

Tradycyjna architektura .NET 4.8 często opierała się na potężnej i zmonolityzowanej bibliotece System.Windows. W nowoczesnym ASP.NET Core sercem aplikacji jest Generic Host oraz modularny rurociąg żądań HTTP (Request Pipeline) ułożony z komponentów Middleware. Żądanie przechodzi przez kolejne filtry, w których każde oprogramowanie pośredniczące może zmodyfikować kontekst, zanim zostanie przekazane do odpowiedniego sterownika lub komponentu renderującego.

Modele uruchomieniowe Blazor

Blazor nie jest pojedynczą technologią, ale modelem komponentowym, który można osadzić w różnych konfiguracjach hostingowych:

  • Blazor Server: Komponenty wykonywane są po stronie serwera wewnątrz aplikacji ASP.NET Core. Komunikacja UI odbywa się poprzez protokół WebSockets z użyciem SignalR. Stan powiązany z połączonym klientem nazywany jest "obwodem" (circuit). Pozwala to na pełny dostęp do zasobów serwera bez wysyłania kodu C# do przeglądarki.
  • Blazor WebAssembly (Wasm): Aplikacja (wraz z runtime'em .NET) pobierana jest do przeglądarki klienta i wykonywana w piaskownicy przeglądarki (sandbox). Model ten umożliwia pełną pracę w trybie offline, w architekturze PWA (Progressive Web App) i zdejmuje obciążenie obliczeniowe z serwera.
  • Blazor Hybrid: Łączy nowoczesne technologie webowe z oprogramowaniem natywnym.

Dobre Praktyki i Antywzorce

W procesie przenoszenia logiki z .NET Framework 4.8 do architektury .NET 10 łatwo popełnić błędy polegające na przenoszeniu dawnych nawyków w nowe ramy technologiczne.

  • Antywzorzec (ZASADA ZERO HYBRYDY W 4.8): Choć w oficjalnej dokumentacji pojawiają się informacje o integracji kontrolek BlazorWebView w celu modernizacji istniejących aplikacji WPF czy Windows Forms, należy to kategorycznie sprostować w odniesieniu do środowisk współdzielonych z .NET 4.8. Użycie natywnego Blazora w "starej" aplikacji Windows Forms w architekturze Framework 4.8 jest niemożliwe; mechanika hybrydowa z BlazorWebView wymaga zastosowania nowoczesnego środowiska uruchomieniowego w projektach natywnych .NET 6+. Migracja musi polegać na wydzieleniu logiki biznesowej do .NET Standard 2.0 i sukcesywnym podpinaniu nowoczesnego front-endu w architekturze .NET 10.
  • Antywzorzec: Blokowanie wątków systemowych (np. Thread.Sleep czy synchroniczne Wait() na zadaniach). Aplikacje ASP.NET Core i serwer Kestrel zostały zoptymalizowane pod kątem tysięcy współbieżnych połączeń dzięki zastosowaniu mechanizmów asynchronicznych. Złamanie tej zasady w architekturze webowej zdestabilizuje serwer. Programowanie musi być w całości oparte o wzorce async/await.
  • Dobra praktyka: Pełna separacja logiki prezentacji (Blazor/API) od logiki biznesowej i dostępu do danych (Entity Framework Core) za sprawą Dependency Injection. W ASP.NET Core wstrzykiwanie zależności nie jest zewnętrznym dodatkiem to wbudowany filar frameworka udostępniany poprzez interfejs WebApplicationBuilder.Services. Umożliwia to zjawisko całkowicie luźnego powiązania (loose coupling) i oddzielenie kodu renderującego UI od operacji I/O.

Laboratorium kodu: Rejestracja usług i budowa komponentu

Poniżej przedstawiono implementację ilustrującą współdziałanie nowej mechaniki konfiguracyjnej oraz architektury asynchronicznej. Rejestracja zależności odbywa się w pliku punktu startowego Program.cs, co zastępuje w całości dawne konstrukcje z wykorzystaniem Global.asax i ciężkich konstruktorów.

// Program.cs w .NET 10 (Uproszczony model Generic Host)
using Microsoft.EntityFrameworkCore;
using ModernApp.Data;

var builder = WebApplication.CreateBuilder(args);

// Rejestracja puli logiki poprzez wbudowany kontener DI
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

// Użycie appsettings.json do odczytu Connection Stringa wstrzykiwanego do EF Core
builder.Services.AddDbContextFactory<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// Konfiguracja potoku żądań Middleware
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

Mechanika po stronie klienta również ulega uproszczeniu, przechodząc z obiektów Dispatcher na zautomatyzowany cykl odświeżania cyklu życia. Poniżej przedstawiony jest w pełni asynchroniczny komponent odczytujący dane z systemu, respektujący zasady modelu bezstanowego po stronie UI:

// TasksList.razor
@page "/tasks"
@inject ITaskService TaskService

<div class="task-board">
    @if (_tasks == null)
    {
        <p><em>Ładowanie strumieniowe, proszę czekać...</em></p>
    }
    else
    {
        <table class="table">
            @foreach (var task in _tasks)
            {
                <tr>
                    <td>@task.Title</td>
                    <td>@task.AssignedTo</td>
                </tr>
            }
        </table>
    }
</div>

@code {
    private List<TaskDto>? _tasks;

    // Asynchroniczny odpowiednik zdarzenia Form_Load / UserControl.Loaded
    protected override async Task OnInitializedAsync()
    {
        // Wywołanie usługi wstrzykniętej z DI - brak blokowania wątku interfejsu (UI Thread)
        _tasks = await TaskService.GetActiveTasksAsync();
    }
}

Wnioski architektoniczne

Porzucenie zmonolityzowanego modelu Stateful (aplikacji stanowych Desktop w .NET 4.8) na rzecz ekosystemu .NET 10, gdzie dominuje bezstanowa architektura potoku żądań (Middleware) i elastyczne hostowanie interfejsu (Blazor Server/WebAssembly), wymaga transformacji sposobu projektowania pamięci i stanu aplikacji. Blazor upodabnia pisanie nowoczesnego, responsywnego interfejsu klienta do modelu podobnego pod względem mentalnym do wzorca znanej abstrakcji klas c#, jednak eliminuje bezpośredni, procesowy dostęp do hardwareu stacji roboczej z poziomu interfejsu, wymuszając w pełni asynchroniczną i bezpieczną integrację poprzez komunikację sieciową (API i potoki wstrzykiwania zależności).