% Options for packages loaded elsewhere \PassOptionsToPackage{unicode}{hyperref} \PassOptionsToPackage{hyphens}{url} \documentclass[ ]{article} \usepackage{xcolor} \usepackage{amsmath,amssymb} \setcounter{secnumdepth}{-\maxdimen} % remove section numbering \usepackage{iftex} \ifPDFTeX \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{textcomp} % provide euro and other symbols \else % if luatex or xetex \usepackage{unicode-math} % this also loads fontspec \defaultfontfeatures{Scale=MatchLowercase} \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1} \fi \usepackage{lmodern} \ifPDFTeX\else % xetex/luatex font selection \fi % Use upquote if available, for straight quotes in verbatim environments \IfFileExists{upquote.sty}{\usepackage{upquote}}{} \IfFileExists{microtype.sty}{% use microtype if available \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} \makeatletter \@ifundefined{KOMAClassName}{% if non-KOMA class \IfFileExists{parskip.sty}{% \usepackage{parskip} }{% else \setlength{\parindent}{0pt} \setlength{\parskip}{6pt plus 2pt minus 1pt}} }{% if KOMA class \KOMAoptions{parskip=half}} \makeatother \usepackage{color} \usepackage{fancyvrb} \newcommand{\VerbBar}{|} \newcommand{\VERB}{\Verb[commandchars=\\\{\}]} \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}} % Add ',fontsize=\small' for more characters per line \newenvironment{Shaded}{}{} \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} \newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} \newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}} \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} \newcommand{\BuiltInTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}} \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}} \newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} \newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}} \newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}} \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}} \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} \newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}} \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} \newcommand{\ExtensionTok}[1]{#1} \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}} \newcommand{\ImportTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}} \newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}} \newcommand{\NormalTok}[1]{#1} \newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}} \newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}} \newcommand{\RegionMarkerTok}[1]{#1} \newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} \newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}} \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} \newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}} \newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} \newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} \usepackage{longtable,booktabs,array} \newcounter{none} % for unnumbered tables \usepackage{calc} % for calculating minipage widths % Correct order of tables after \paragraph or \subparagraph \usepackage{etoolbox} \makeatletter \patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} \makeatother % Allow footnotes in longtable head/foot \IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}} \makesavenoteenv{longtable} \setlength{\emergencystretch}{3em} % prevent overfull lines \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} \usepackage{bookmark} \IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available \urlstyle{same} \hypersetup{ pdftitle={manuscript}, hidelinks, pdfcreator={LaTeX via pandoc}} \title{manuscript} \author{} \date{} \begin{document} \maketitle { \setcounter{tocdepth}{3} \tableofcontents } \section{Architektura ASP.NET Core i Ekosystem Blazor}\label{architektura-asp.net-core-i-ekosystem-blazor} Programiści aplikacji WinForms i WPF od lat budują interfejsy w oparciu o stanowe kontrolki użytkownika i wzorzec MVVM. Nowoczesny ekosystem platformy .NET pozwala ujednolicić i zmodernizować ten proces za pomocą frameworka Blazor, działającego na fundamentach ASP.NET Core. Blazor opiera się na języku C\# zamiast na JavaScripcie, oferując spójny, oparty na komponentach i zdarzeniach model programowania, który z perspektywy dewelopera aplikacji desktopowych będzie wysoce znajomy. \paragraph{Mapowanie pojęć: Desktop vs Web}\label{mapowanie-pojux119ux107-desktop-vs-web} Migracja do nowej platformy wymaga redefinicji klasycznych wzorców. Tabela poniżej przedstawia bezpośrednie zestawienie paradygmatów: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w ASP.NET Core / Blazor & Opis zmiany \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{UserControl} & \textbf{Komponent Razor} & Logika i widok są hermetyzowane w reużywalnych plikach \texttt{.razor}. Renderowanie interfejsu opiera się na wydajnym mechanizmie różnicowym (diff-based). \\ \textbf{\texttt{app.config} / \texttt{web.config}} & \textbf{\texttt{appsettings.json}} & Przejście na lekką, hierarchiczną konfigurację w formacie JSON, która wspiera nadpisywanie zmiennych dla różnych środowisk (Development, Production). \\ \textbf{Wstrzykiwanie zależności (DI)} & \textbf{Wbudowany kontener DI} & Standaryzacja DI jako natywnego mechanizmu frameworka ASP.NET Core, który w .NET 4.8 często wymagał zewnętrznych bibliotek. \\ \textbf{Cykl życia okna} & \textbf{Cykl życia żądania / Potok Middleware} & W aplikacjach webowych żądania HTTP i stan logiki są przetwarzane przez konfigurowalną sekwencję komponentów (Middleware). \\ \end{longtable} } \paragraph{Architektura systemowa: Zarządzanie stanem i piaskownica środowiska}\label{architektura-systemowa-zarzux105dzanie-stanem-i-piaskownica-ux15brodowiska} Główną różnicą między architekturą „Heavy Client'' (WPF) a nowoczesnymi rozwiązaniami webowymi jest sposób zarządzania stanem, który w środowisku internetowym bywa rozproszony i asynchroniczny. W zależności od wymagań aplikacji, framework Blazor udostępnia różne modele hostingu: \begin{itemize} \tightlist \item \textbf{Blazor Server:} Komponenty wykonywane są na serwerze w ramach aplikacji ASP.NET Core. Aktualizacje UI, obsługa zdarzeń i wywołania JavaScriptu przesyłane są asynchronicznie za pomocą protokołu WebSockets i połączenia SignalR. Stan skojarzony z każdym połączonym klientem (tzw. obwód, ang. \emph{circuit}) znajduje się w pamięci serwera. Gwarantuje to pełną kompatybilność z API .NET i bezpośredni dostęp do zasobów serwera. \item \textbf{Blazor WebAssembly:} Środowisko uruchomieniowe .NET i komponenty pobierane są do przeglądarki klienta, gdzie działają bezpośrednio w oparciu o technologię WebAssembly. Znacząco różni się to od rozwiązań desktopowych -- z uwagi na ograniczenia bezpieczeństwa (piaskownica przeglądarki), komponenty nie mają bezpośredniego dostępu do lokalnego systemu plików czy bazy danych. Wymagana jest wtedy pośrednia komunikacja asynchroniczna z serwerowymi API (np. REST). \end{itemize} \paragraph{Strategia Migracji: Podejście Blazor Hybrid}\label{strategia-migracji-podejux15bcie-blazor-hybrid} Dla doświadczonych zespołów rozwijających systemy desktopowe przepisywanie całych aplikacji od zera (tzw. Big Bang rewrite) narzuca ogromne ryzyko i koszty operacyjne. Naturalną ścieżką ewolucji, realizującą strategię \emph{In-place upgrade}, jest wykorzystanie architektury \textbf{Blazor Hybrid}. W modelu hybrydowym komponenty Razor nie działają poprzez WebAssembly. Zamiast tego są uruchamiane natywnie wewnątrz istniejącej aplikacji WPF lub WinForms, współpracując bezpośrednio z osadzonym oknem przeglądarki za pośrednictwem lokalnej kontrolki \texttt{BlazorWebView}. \textbf{Wnioski implementacyjne z modelu hybrydowego:} \begin{itemize} \tightlist \item \textbf{Pełny dostęp do zasobów:} W przeciwieństwie do ograniczeń przeglądarki (sandbox), aplikacje Blazor Hybrid działają z natywnymi prawami systemu operacyjnego. Zapewniają pełny dostęp do sprzętu i zasobów systemu plików za pośrednictwem klasycznych API .NET. \item \textbf{Sukcesywne przenoszenie UI:} Programiści mogą krok po kroku przepisywać ekrany z XAML na komponenty Razor. Utworzone w ten sposób widoki mogą być później łatwo współdzielone w innych środowiskach, na przykład w czystych projektach webowych na Blazor WebAssembly. \end{itemize} \paragraph{Laboratorium kodu: Inicjalizacja usług i potoku}\label{laboratorium-kodu-inicjalizacja-usux142ug-i-potoku} W platformie ASP.NET Core tradycyjne pliki \texttt{Global.asax} zostają zastąpione zwięzłym API w pliku \texttt{Program.cs}. Obiekt \texttt{WebApplicationBuilder} przejmuje pełną odpowiedzialność za inicjalizację kontenera zależności (DI), logowanie i konfigurację hosta. Poniżej przedstawiono standardowy proces rejestracji komponentów webowych: \begin{Shaded} \begin{Highlighting}[] \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// 1. Inicjalizacja kontenera DI i zasilenie go konfiguracją z appsettings.json} \NormalTok{builder.Services.AddRazorComponents()} \NormalTok{ .AddInteractiveServerComponents();} \NormalTok{var app = builder.Build();} \NormalTok{// 2. Potok Middleware {-} definicja zachowań w zależności od środowiska} \NormalTok{if (!app.Environment.IsDevelopment())} \NormalTok{\{} \NormalTok{ app.UseExceptionHandler("/Error", createScopeForErrors: true);} \NormalTok{ app.UseHsts(); // Wymuszenie protokołu HTTPS} \NormalTok{\}} \NormalTok{app.UseHttpsRedirection();} \NormalTok{app.UseStaticFiles(); // Middleware serwujący pliki statyczne} \NormalTok{app.UseAntiforgery();} \NormalTok{// 3. Mapowanie ruterów dla stworzonych komponentów} \NormalTok{app.MapRazorComponents\textless{}App\textgreater{}()} \NormalTok{ .AddInteractiveServerRenderMode();} \NormalTok{app.Run();} \end{Highlighting} \end{Shaded} W nowych środowiskach doświadczenie nabyte w pracy z MVVM bezpośrednio procentuje. Reaktywność interfejsu (znana z interfejsu \texttt{INotifyPropertyChanged}) jest w Blazor ustandaryzowana przez wywoływanie zdarzeń cyklu życia, takich jak metoda \texttt{StateHasChanged}, podczas gdy dostęp do serwisów biznesowych realizowany jest po prostu przez wbudowaną dyrektywę \texttt{@inject}. Umożliwia to zachowanie rygorystycznej separacji pomiędzy warstwą logiki biznesowej a nową, wieloplatformową warstwą prezentacji. \section{Fundamenty Architektury -- Od Desktopu do ASP.NET Core i Blazor}\label{fundamenty-architektury-od-desktopu-do-asp.net-core-i-blazor} Przejście z klasycznych aplikacji desktopowych (WinForms / WPF) w środowisku .NET Framework 4.8 do nowoczesnego ekosystemu webowego i hybrydowego w oparciu o ASP.NET Core oraz framework Blazor wymaga przyswojenia nowych fundamentów architektonicznych. W architekturze "Heavy Client" punktem wyjścia była pętla zdarzeń systemu Windows oraz scentralizowany stan w pamięci aplikacji. Nowoczesne fundamenty .NET opierają się z kolei na asynchronicznym potoku przetwarzania żądań, wbudowanym mechanizmie wstrzykiwania zależności oraz modułowym hoście. \paragraph{1. Mapowanie pojęć: Desktop vs ASP.NET Core}\label{mapowanie-pojux119ux107-desktop-vs-asp.net-core} Migracja architektury wymaga zrozumienia, jak klasyczne mechanizmy z aplikacji okienkowych przekładają się na usługi ASP.NET Core: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w ASP.NET Core / Blazor & Opis zmiany architektonicznej \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{\texttt{App.config} / Settings} & \textbf{\texttt{appsettings.json}} & Przejście z XML na lekką, hierarchiczną konfigurację w formacie JSON, która natywnie obsługuje różne środowiska (np. \texttt{Development}, \texttt{Production}) i zmienne środowiskowe. \\ \textbf{Brak natywnego DI / Zewnętrzne kontenery} & \textbf{Wbudowany kontener DI} & Standaryzacja mechanizmu Dependency Injection (DI) na poziomie frameworka. Usługi rejestruje się globalnie, co całkowicie zastępuje wzorce takie jak Service Locator z czasów .NET 4.8. \\ \textbf{\texttt{App.xaml.cs} (Cykl życia)} & \textbf{\texttt{Program.cs} (Minimal Host)} & Kod startowy aplikacji został uproszczony do konfiguracji obiektu \texttt{WebApplicationBuilder}, który inicjalizuje usługi i potok przetwarzania. \\ \textbf{Zdarzenia okna / Global.asax} & \textbf{Potok Middleware} & Przetwarzanie żądań jest realizowane przez elastyczny łańcuch komponentów (Middleware), z których każdy może zmodyfikować żądanie lub skrócić potok. \\ \end{longtable} } \paragraph{2. Architektura systemowa: Potok Middleware, Host i Zarządzanie Stanem}\label{architektura-systemowa-potok-middleware-host-i-zarzux105dzanie-stanem} Aplikacje oparte na ASP.NET Core są zbudowane wokół koncepcji \textbf{Hosta}. Host ten enkapsuluje wszystkie zasoby systemu, takie jak serwer HTTP (np. wieloplatformowy serwer Kestrel), mechanizmy logowania, kontener usług (DI) oraz konfigurację. W przeciwieństwie do monolitycznej natury aplikacji WPF, platforma webowa wymusza rozproszenie stanu. W rozwiązaniach opartych na \textbf{Blazor Server}, komponenty i logika biznesowa wykonują się po stronie serwera wewnątrz aplikacji ASP.NET Core. Stan każdego użytkownika jest przechowywany w pamięci jako tzw. obwód (ang. \emph{circuit}), a aktualizacje interfejsu (DOM) przesyłane są w czasie rzeczywistym za pomocą połączenia SignalR. Z kolei w modelu \textbf{Blazor WebAssembly}, środowisko uruchomieniowe .NET jest pobierane bezpośrednio do przeglądarki. Fundamentalną zmianą dla deweloperów desktopowych jest tutaj bezpieczeństwo: kod działa w zamkniętej piaskownicy przeglądarki (sandbox), co odcina bezpośredni dostęp do systemu plików czy rejestru Windows. Komunikacja ze światem zewnętrznym odbywa się w tym modelu wyłącznie asynchronicznie poprzez zabezpieczone serwerowe interfejsy API. Pojęcie pętli zdarzeń znane z WinForms zostaje zastąpione przez \textbf{potok Middleware}. Każde żądanie przechodzi przez kolejne oprogramowanie pośredniczące, które wykonuje wybrane operacje (np. autoryzacja, routing, logowanie) i decyduje, czy przekazać kontekst (\texttt{HttpContext}) do kolejnego elementu. \paragraph{3. Laboratorium kodu: Inicjalizacja Hosta i Konfiguracja Potoku}\label{laboratorium-kodu-inicjalizacja-hosta-i-konfiguracja-potoku} W nowoczesnym środowisku .NET inicjalizacja aplikacji odbywa się z poziomu jednego pliku \texttt{Program.cs}. Znane z aplikacji WPF ręczne instancjonowanie okien i wstrzykiwanie widoków zastępuje rejestracja komponentów systemowych. Analiza przypadku -- Rejestracja usług i middleware dla aplikacji Blazor: \begin{Shaded} \begin{Highlighting}[] \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// Wstrzykiwanie Zależności (DI) {-} nowoczesny odpowiednik wzorca z WPF} \NormalTok{// Rejestracja własnego kontekstu bazy danych oraz usług komponentów Blazor} \NormalTok{builder.Services.AddDbContextFactory\textless{}AppDbContext\textgreater{}(options =\textgreater{} } \NormalTok{ options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));} \NormalTok{builder.Services.AddRazorComponents()} \NormalTok{ .AddInteractiveServerComponents(); // Wskazanie modelu Blazor Server} \NormalTok{// Zbudowanie Hosta (Host enkapsuluje serwer Kestrel, logowanie i konfigurację)} \NormalTok{var app = builder.Build();} \NormalTok{// Architektura Middleware {-} definiowanie potoku} \NormalTok{// Zabezpieczenie środowiskowe: szczegółowe logi błędu tylko w Development} \NormalTok{if (!app.Environment.IsDevelopment()) } \NormalTok{\{} \NormalTok{ app.UseExceptionHandler("/Error", createScopeForErrors: true);} \NormalTok{ app.UseHsts(); } \NormalTok{\}} \NormalTok{app.UseHttpsRedirection();} \NormalTok{app.UseStaticFiles(); // Middleware udostępniający zasoby (CSS, pliki JS)} \NormalTok{app.UseAntiforgery();} \NormalTok{// Mapowanie komponentów {-} serwer zaczyna reagować na ruting UI} \NormalTok{app.MapRazorComponents\textless{}App\textgreater{}()} \NormalTok{ .AddInteractiveServerRenderMode();} \NormalTok{app.Run(); // Uruchomienie pętli nasłuchującej} \end{Highlighting} \end{Shaded} W powyższym fragmencie wykorzystano również natywny mechanizm wstrzykiwania zależności (\texttt{@inject} w widokach), który standaryzuje to, co w aplikacjach WPF osiągano za pomocą rozwiązań zewnętrznych przy architekturze MVVM. \paragraph{4. Strategia Migracji: Podejście Blazor Hybrid}\label{strategia-migracji-podejux15bcie-blazor-hybrid-1} Ze względu na drastyczne różnice w dostępie do zasobów maszyny (piaskownica webowa vs natywne uprawnienia Windows) oraz zmianę paradygmatu zarządzania stanem, natychmiastowe przepisanie dużej aplikacji WPF do czystego środowiska Web bywa kosztowne i ryzykowne. Z pomocą przychodzi tu \textbf{Blazor Hybrid}. W modelu hybrydowym, komponenty interfejsu (Razor) nie są uruchamiane na serwerze ani za pośrednictwem WebAssembly. Zamiast tego wykonują się one natywnie wewnątrz istniejącej aplikacji desktopowej (WPF, WinForms lub .NET MAUI) wykorzystując wbudowaną kontrolkę \texttt{BlazorWebView}. \textbf{Wnioski implementacyjne dla Blazor Hybrid:} \begin{itemize} \tightlist \item \textbf{Bezpieczeństwo i Dostęp:} Aplikacje hybrydowe omijają restrykcje piaskownicy przeglądarki. Interfejs jest renderowany w technologiach webowych (HTML/CSS), ale kod logiki biznesowej C\# współdzieli natywny proces aplikacji WPF, oferując bezpośredni i pełny dostęp do systemu plików, lokalnych baz danych i sprzętu za pośrednictwem zwykłego API platformy .NET. \item \textbf{Współdzielenie komponentów:} Przyjęcie tego modelu pozwala zespołom programistycznym stopniowo "odcinać" stare widoki XAML i zastępować je komponentami Razor w obrębie tej samej instancji programu. Nowo powstałe kontrolki można docelowo przenieść bez zmian do aplikacji Blazor WebAssembly, co realizuje założenia ewolucyjnej modernizacji systemów klasy Enterprise. \end{itemize} \section{Fundamenty Architektury -- Koncepcja Generic Host}\label{fundamenty-architektury-koncepcja-generic-host} W klasycznych aplikacjach desktopowych (WPF, WinForms) cykl życia programu był nierozerwalnie związany z głównym oknem interfejsu użytkownika oraz pętlą zdarzeń (np. plik \texttt{App.xaml.cs} i metoda \texttt{Application.Run()}). Modernizacja architektury i przejście do nowoczesnego ekosystemu .NET wymaga odłączenia logiki uruchomieniowej od warstwy prezentacji. Służy temu mechanizm \textbf{Hosta} (ang. \emph{Host}), który stanowi ewolucyjny fundament zarządzania zasobami i cyklem życia nowoczesnych aplikacji. Na etapie uruchamiania, nowoczesna aplikacja ASP.NET Core buduje Hosta, który w jednym miejscu enkapsuluje wszystkie kluczowe zasoby systemu, takie jak serwer HTTP, oprogramowanie pośredniczące (Middleware), mechanizmy logowania, usługi wstrzykiwania zależności (DI) oraz konfigurację środowiska. \paragraph{1. Mapowanie pojęć: Od Desktopu do Hosta}\label{mapowanie-pojux119ux107-od-desktopu-do-hosta} Zrozumienie koncepcji Hosta wymaga zmapowania mechanizmów znanych z .NET Framework 4.8 na ich nowoczesne odpowiedniki bazujące na wzorcu \textbf{Generic Host}: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w architekturze .NET (Host) & Opis zmiany architektonicznej \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{\texttt{Application.Startup} / \texttt{App.xaml.cs}} & \textbf{\texttt{Program.cs} / \texttt{WebApplicationBuilder}} & Inicjalizacja całej aplikacji przeniesiona jest do zwięzłego interfejsu budowniczego (Builder), który ujednolica konfigurację. \\ \textbf{Pętla komunikatów UI (\texttt{Dispatcher})} & \textbf{\texttt{app.Run()} / Serwer HTTP (Kestrel)} & Zamiast nasłuchiwać zdarzeń myszy i klawiatury, Host uruchamia serwer Kestrel nasłuchujący asynchronicznych żądań HTTP. \\ \textbf{Klasy statyczne / Service Locator} & \textbf{Wbudowany kontener DI} & Host automatycznie zarządza cyklem życia rejestrowanych usług (Singleton, Scoped, Transient), zastępując statyczne menedżery czy zewnętrzne kontenery IoC. \\ \textbf{Wątki w tle (BackgroundWorker)} & \textbf{\texttt{IHostedService} (Scenariusze Non-Web)} & Długotrwałe procesy w tle są zarządzane i bezpiecznie zamykane w ramach cyklu życia Hosta jako tzw. Hosted Services. \\ \end{longtable} } \paragraph{2. Architektura systemowa: Rodzaje Hostów i Generic Host}\label{architektura-systemowa-rodzaje-hostuxf3w-i-generic-host} W historii platformy ASP.NET Core istniało kilka wariantów hostowania, w tym przestarzały już \texttt{WebHost} zachowany jedynie dla kompatybilności wstecznej. Obecnym standardem systemowym jest \textbf{.NET Generic Host} (często wykorzystywany poprzez klasę \texttt{WebApplication}, zwaną \emph{Minimal Host}). Klasa \texttt{WebApplication} zachowuje się analogicznie do klasycznego \emph{Generic Host}, eksponując te same interfejsy, ale drastycznie redukuje ilość kodu wymaganego do jej konfiguracji. Obiekt \texttt{WebApplicationBuilder}, podczas swojego tworzenia (\texttt{Build()}), nakłada na aplikację zestaw domyślnych, zoptymalizowanych ustawień: \begin{itemize} \tightlist \item Integruje i uruchamia wieloplatformowy serwer Kestrel. \item Ładuje hierarchiczną konfigurację bazującą na plikach \texttt{appsettings.json}, zmiennych środowiskowych i argumentach wiersza poleceń. \item Konfiguruje dostawców logowania (kierując logi domyślnie m.in. do konsoli i okna Debug). \end{itemize} \paragraph{3. Laboratorium kodu: Inicjalizacja Minimal Host}\label{laboratorium-kodu-inicjalizacja-minimal-host} W nowoczesnych rozwiązaniach C\# punktem wejścia do aplikacji jest wysoce zoptymalizowany plik \texttt{Program.cs}. Dla programistów przenoszących kod z WPF, to miejsce stanowi odpowiednik jednoczesnego konfigurowania App.config, kontenera IoC i okna głównego. \begin{Shaded} \begin{Highlighting}[] \NormalTok{// 1. Inicjalizacja wzorca Builder dla Hosta} \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// 2. Rejestracja usług w kontenerze Dependency Injection Hosta} \NormalTok{// Odpowiednik rejestrowania ViewModeli i serwisów w zewnętrznych bibliotekach (np. Autofac) w WPF} \NormalTok{builder.Services.AddControllersWithViews(); } \NormalTok{builder.Services.AddRazorPages();} \NormalTok{// 3. Utworzenie instancji Hosta ze zdefiniowanymi usługami} \NormalTok{var app = builder.Build();} \NormalTok{// 4. Konfiguracja potoku żądań (Middleware) w zbudowanym Hoście} \NormalTok{if (!app.Environment.IsDevelopment())} \NormalTok{\{} \NormalTok{ app.UseExceptionHandler("/Error"); // Centralne zarządzanie błędami} \NormalTok{ app.UseHsts();} \NormalTok{\}} \NormalTok{app.UseHttpsRedirection();} \NormalTok{app.UseStaticFiles();} \NormalTok{app.UseRouting();} \NormalTok{app.UseAuthorization();} \NormalTok{// 5. Uruchomienie pętli głównej Hosta (serwer rozpoczyna nasłuchiwanie)} \NormalTok{app.Run();} \end{Highlighting} \end{Shaded} \emph{Analiza przypadku:} Wywołanie metody \texttt{Run()} usypia główny wątek, pozwalając serwerowi HTTP (lub usługom w tle) przejąć kontrolę nad procesem. Stanowi to bezpośredni odpowiednik uruchomienia głównej pętli zdarzeń w natywnej aplikacji Windows. \paragraph{4. Strategia Migracji: Separacja Logiki i Scenariusze Non-Web (Background Tasks)}\label{strategia-migracji-separacja-logiki-i-scenariusze-non-web-background-tasks} Jednym z najważniejszych wniosków implementacyjnych dla zespołów migrujących systemy z Windows Forms i WPF jest fakt, że nowoczesny \textbf{Generic Host pozwala na tworzenie aplikacji, które nie są aplikacjami webowymi}. Architektura Generic Host ułatwia innym typom aplikacji (np. konsolowym czy procesom backendowym) korzystanie z wieloprzekrojowych rozszerzeń frameworka (takich jak logowanie, wstrzykiwanie zależności, konfiguracja, czy zarządzanie cyklem życia). Zamiast utrzymywać skomplikowaną logikę biznesową polegającą na odpytywaniu bazy danych (polling) w ukrytym oknie WPF czy za pomocą klasycznego obiektu \texttt{Timer} uwięzionego w kodzie interfejsu (Code-Behind), strategia migracji zakłada ekstrakcję tego kodu. Logikę przenosi się do klas implementujących interfejs \texttt{IHostedService} uruchamianych wewnątrz lekkiego procesu opartego na \texttt{.NET\ Generic\ Host}. Zapewnia to architekturę gotową do pracy jako rozproszony mikrokonsument lub usługa systemu Windows, gwarantując profesjonalne zarządzanie błędami i pełną separację od warstwy UI. \section{Zarządzanie cyklem życia w architekturze Generic Host}\label{zarzux105dzanie-cyklem-ux17cycia-w-architekturze-generic-host} W klasycznych aplikacjach Windows (WinForms, WPF) cykl życia programu był ściśle sprzężony z głównym oknem interfejsu użytkownika oraz pętlą zdarzeń systemu operacyjnego (np. \texttt{Application.Run()}). Nowoczesny ekosystem platformy .NET rozdziela warstwę prezentacji od logiki uruchomieniowej aplikacji poprzez wykorzystanie wzorca \textbf{Generic Host}. W tej architekturze to obiekt Hosta przejmuje pełną odpowiedzialność za zarządzanie całkowitym czasem życia aplikacji, inicjalizację procesów w tle oraz kontrolę pamięci. \paragraph{Mapowanie pojęć: Desktop vs ASP.NET Core}\label{mapowanie-pojux119ux107-desktop-vs-asp.net-core-1} Zrozumienie zmiany paradygmatu wymaga zmapowania mechanizmów desktopowych na scentralizowany system oparty o Generic Host i wbudowane kontenery odwrócenia kontroli (IoC): {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w architekturze Generic Host & Opis zmiany architektonicznej \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{Zdarzenia \texttt{App.Startup} / \texttt{App.Exit}} & \textbf{Cykl życia Hosta (App Lifetime)} & Przeniesienie odpowiedzialności za start i zamknięcie programu z klasy Application na wbudowany mechanizm zarządzania czasem życia aplikacji w obiekcie Host. \\ \textbf{Pętla komunikatów UI} & \textbf{Serwer HTTP / Żądania asynchroniczne} & Działanie programu nie zależy od nasłuchiwania myszy/klawiatury, lecz od Hosta utrzymującego proces nasłuchujący, takiego jak serwer Kestrel. \\ \textbf{Ręczne wywoływanie \texttt{Dispose()}} & \textbf{Kontener Wstrzykiwania Zależności (DI)} & Host zarządza inicjalizacją i zwalnianiem obiektów. Na przykład kontrolery Web API są aktywowane i poddawane utylizacji dla każdego pojedynczego żądania (per request basis). \\ \textbf{\texttt{BackgroundWorker} / \texttt{DispatcherTimer}} & \textbf{Usługi \texttt{IHostedService}} & Generic Host integruje zadania w tle jako odizolowane procesy współdzielące wspólny cykl życia Hosta, niezależne od warstwy sieciowej. \\ \end{longtable} } \paragraph{Architektura systemowa: Centralizacja zasobów i potok żądań}\label{architektura-systemowa-centralizacja-zasobuxf3w-i-potok-ux17cux105daux144} Na etapie rozruchu, Host enkapsuluje wszystkie zasoby systemowe, włączając w to kontener wstrzykiwania zależności (DI), konfigurację środowiskową, logowanie i docelowo potok oprogramowania pośredniczącego (Middleware). Fundamentalną różnicą względem aplikacji desktopowych jest rozproszenie i kaskadowość cyklu życia. Poza globalnym cyklem życia samego Hosta (start i bezpieczne wyłączenie procesu), platforma wprowadza bardzo rygorystyczny cykl życia pojedynczych żądań sieciowych oraz obiektów wewnątrz nich: \begin{itemize} \tightlist \item \textbf{Komponenty oparte na konwencjach (Convention-based Middleware):} Instancjonowane tylko raz przy uruchomieniu aplikacji i funkcjonujące jako Singleton przez cały cykl życia Hosta. \item \textbf{Komponenty implementujące interfejs \texttt{IMiddleware}:} Fabryka instancjonuje je oddzielnie dla każdego nadchodzącego żądania HTTP (tzw. cykl \emph{Scoped}), co pozwala bezpiecznie wstrzykiwać do nich lokalne usługi i kontekst bazy danych. \item \textbf{Scenariusze non-web (Usługi w tle):} Host dostarcza uniwersalny interfejs umożliwiający korzystanie z globalnego zarządzania cyklem życia oraz logowania w aplikacjach, które wcale nie odbierają żądań HTTP (np. mikrousługi wykonujące asynchroniczną analizę danych). \end{itemize} \paragraph{Laboratorium kodu: Inicjalizacja cyklu życia usług i żądań}\label{laboratorium-kodu-inicjalizacja-cyklu-ux17cycia-usux142ug-i-ux17cux105daux144} Poniżej przedstawiono proces uruchamiania minimalnego Hosta (\texttt{WebApplication}) wraz z definicją cyklu życia jego usług: \begin{Shaded} \begin{Highlighting}[] \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// 1. Zarządzanie cyklem życia przez kontener DI} \NormalTok{// Rejestracja kontrolerów, które zostaną aktywowane dla każdego żądania } \NormalTok{builder.Services.AddControllers();} \NormalTok{// Rejestracja usług biznesowych} \NormalTok{// W WPF wymagałoby to ręcznego cyklu życia lub np. zewnętrznego Autofac} \NormalTok{builder.Services.AddScoped\textless{}IMyBusinessLogic, MyBusinessLogic\textgreater{}(); // Czas życia ograniczony do jednego żądania} \NormalTok{var app = builder.Build();} \NormalTok{// 2. Cykl życia potoku przetwarzania (Middleware)} \NormalTok{// Rejestracja inline middleware, który przekaże kontrolę dalej} \NormalTok{app.Use(async (context, next) =\textgreater{} \{} \NormalTok{ // Rozpoczęcie przetwarzania} \NormalTok{ await next();} \NormalTok{ // Powrót w potoku po obsłużeniu cyklu życia danego żądania} \NormalTok{\});} \NormalTok{// Zmapowanie kontrolerów } \NormalTok{app.MapControllers();} \NormalTok{// 3. Uruchomienie głównej pętli Hosta} \NormalTok{// Metoda Run() podtrzymuje proces i zarządza sygnałami zakończenia działania} \NormalTok{app.Run();} \end{Highlighting} \end{Shaded} \paragraph{Strategia Migracji: Zabezpieczenie zadań w tle (Background Tasks)}\label{strategia-migracji-zabezpieczenie-zadaux144-w-tle-background-tasks} W projektach bazujących na WinForms lub WPF, programiści bardzo często wykorzystują niewidzialne okna (tzw. okna narzędziowe) lub komponenty typu \texttt{Timer} powiązane z warstwą prezentacji (tzw. pętlą UI) w celu cyklicznego odpytywania bazy danych, synchronizacji plików lub wykonywania innej logiki biznesowej. Podejście to uniemożliwia proste przejście na architekturę backendową lub chmurową. Wzorzec \textbf{Generic Host} stanowi naturalną strategię modernizacji dla takich mechanizmów, ponieważ z założenia wspomaga tworzenie procesów tła dla scenariuszy nie-webowych (non-web scenarios). Zamiast polegać na warstwie prezentacji, programiści powinni dokonać ekstrakcji cyklicznych procedur do odizolowanych klas implementujących interfejs \texttt{IHostedService}. W ten sposób zarządzanie całkowitym cyklem uruchomienia, podtrzymania i, co niezwykle istotne, przewidywalnego i prawidłowego zamknięcia programu zostaje w pełni zestandaryzowane i zintegrowane w rdzeniu nowoczesnej platformy .NET. Taki kod można następnie użyć zarówno jako niezależnej usługi w środowisku Blazor Server, jak i mikroserwisu pracującego bez żadnego interfejsu graficznego. \section{Wbudowane wstrzykiwanie zależności (DI) w architekturze Generic Host}\label{wbudowane-wstrzykiwanie-zaleux17cnoux15bci-di-w-architekturze-generic-host} W klasycznych systemach tworzonych w .NET Framework 4.8 wstrzykiwanie zależności (Dependency Injection) często wymagało integracji zewnętrznych bibliotek (takich jak Autofac, Ninject czy Unity) i ręcznego zarządzania cyklem życia komponentów. W nowoczesnym ekosystemie .NET mechanizm ten został zestandaryzowany i głęboko zintegrowany z architekturą opartą na wzorcu \textbf{Generic Host}. Platforma ASP.NET Core zawiera wbudowany framework DI, który udostępnia skonfigurowane usługi we wszystkich warstwach aplikacji. Zrozumienie, jak Generic Host enkapsuluje i zarządza zależnościami, stanowi fundament projektowania skalowalnych rozwiązań wieloplatformowych. \paragraph{1. Mapowanie pojęć: Desktop vs ASP.NET Core DI}\label{mapowanie-pojux119ux107-desktop-vs-asp.net-core-di} Implementacja wbudowanego mechanizmu DI wymusza zmianę podejścia do tworzenia i dostarczania usług biznesowych: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w .NET (Generic Host) & Opis zmiany architektonicznej \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{Zewnętrzny kontener IoC} & \textbf{Wbudowany kontener DI} & Standaryzacja mechanizmu Inversion of Control na poziomie całej platformy. Zewnętrzne kontenery można wciąż integrować, lecz wbudowany mechanizm zazwyczaj pokrywa pełne zapotrzebowanie aplikacji. \\ \textbf{Service Locator / Inicjalizacja ręczna} & \textbf{Wstrzykiwanie przez konstruktor (Constructor Injection)} & Klasy definiują swoje zależności w konstruktorze, a framework DI automatycznie dostarcza odpowiednie instancje w czasie działania programu. \\ \textbf{Statyczne instancje w pamięci aplikacji} & \textbf{Cykl życia zarządzany przez Hosta (Singleton, Scoped, Transient)} & Host kontroluje czas życia obiektów, zapewniając automatyczną inicjalizację oraz bezpieczne uwalnianie zasobów po zakończeniu żądania lub pracy aplikacji. \\ \end{longtable} } \paragraph{2. Architektura systemowa: Host jako zarządca usług}\label{architektura-systemowa-host-jako-zarzux105dca-usux142ug} W nowoczesnych rozwiązaniach systemowych punktem centralnym cyklu życia programu jest tzw. Host. Na etapie uruchamiania aplikacji, Host ładuje konfigurację i inicjalizuje scentralizowany kontener wstrzykiwania zależności. Oznacza to, że wszystkie zasoby -- od logowania po konteksty bazy danych -- są rejestrowane i przechowywane w jednym, zarządzanym przez Hosta obiekcie \texttt{IServiceCollection}. Kluczową zaletą tego paradygmatu dla twórców oprogramowania desktopowego jest uniwersalność: wzorzec Generic Host umożliwia używanie mechanizmu DI, zarządzania cyklem życia i logowania w aplikacjach, które nie posiadają interfejsu webowego (scenariusze non-web). Pozwala to na wydzielenie logiki, która w aplikacjach WPF często znajdowała się w zdarzeniach UI, do niezależnych procesów w tle (tzw. \texttt{IHostedService}). Jeśli jednak wbudowany kontener Inversion of Control (IoC) nie spełnia wszystkich specyficznych wymagań zaawansowanej architektury przedsiębiorstwa, platforma wciąż pozwala na jego zastąpienie kontenerem firmy trzeciej. \paragraph{3. Laboratorium kodu: Rejestracja i wstrzykiwanie usług}\label{laboratorium-kodu-rejestracja-i-wstrzykiwanie-usux142ug} Proces konfigurowania aplikacji został drastycznie uproszczony dzięki obiektowi \texttt{WebApplicationBuilder} (który zachowuje się analogicznie do .NET Generic Host, wymagając mniejszej liczby wywołań zwrotnych). Kod konfiguracyjny (dawniej dodawany m.in. w pliku \texttt{Startup.cs} lub \texttt{App.xaml.cs}) rejestruje usługi przy użyciu właściwości \texttt{builder.Services}. \textbf{Analiza przypadku -- Rejestracja w kontenerze Hosta:} \begin{Shaded} \begin{Highlighting}[] \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// Rejestracja klasycznych usług ASP.NET Core} \NormalTok{builder.Services.AddRazorPages();} \NormalTok{builder.Services.AddControllersWithViews();} \NormalTok{// Rejestracja kontekstu bazy danych (EF Core)} \NormalTok{builder.Services.AddDbContextFactory\textless{}BlazorWebAppMoviesContext\textgreater{}(options =\textgreater{}} \NormalTok{ options.UseSqlServer(builder.Configuration.GetConnectionString("MoviesContext")));} \NormalTok{// Rejestracja usług interfejsu (Blazor)} \NormalTok{builder.Services.AddRazorComponents()} \NormalTok{ .AddInteractiveServerComponents();} \NormalTok{// Host buduje kontener i staje się gotowy do pracy} \NormalTok{var app = builder.Build();} \end{Highlighting} \end{Shaded} \emph{Powyższy fragment ukazuje, jak metoda \texttt{CreateBuilder} dostarcza usługi takie jak konfiguracja czy logowanie, do których następnie programista dopina własne abstrakcje, np. kontekst bazy danych.} \textbf{Wstrzykiwanie w warstwie prezentacji (Blazor):} W tradycyjnym wzorcu MVVM w środowisku WPF powiązanie ViewModelu z widokiem (DataContext) wymagało nierzadko tworzenia fabryk lub statycznego rozwiązywania zależności. W systemie komponentowym Blazor, usługi wyciągane są z kontenera DI natywnie w fazie renderowania za pomocą prostej dyrektywy \texttt{@inject}. \begin{Shaded} \begin{Highlighting}[] \NormalTok{@page "/movies"} \NormalTok{@rendermode InteractiveServer} \NormalTok{@using Microsoft.EntityFrameworkCore} \NormalTok{@inject IDbContextFactory\textless{}BlazorWebAppMoviesContext\textgreater{} DbFactory} \NormalTok{\textless{}h1\textgreater{}Index\textless{}/h1\textgreater{}} \NormalTok{@code \{} \NormalTok{ private BlazorWebAppMoviesContext context = default!;} \NormalTok{ // Instancja bazy danych zostaje dostarczona do komponentu przez Hosta} \NormalTok{ protected override void OnInitialized()} \NormalTok{ \{} \NormalTok{ context = DbFactory.CreateDbContext();} \NormalTok{ \}} \NormalTok{\}} \end{Highlighting} \end{Shaded} W kodzie niezwiązanym z interfejsem graficznym, takim jak kontrolery czy strony Razor Pages, standardowo wykorzystuje się metodę wstrzykiwania przez konstruktor. Framework samodzielnie dopasowuje parametry konstruktora do zarejestrowanych usług. \paragraph{4. Strategia Migracji: DI w środowisku Blazor Hybrid}\label{strategia-migracji-di-w-ux15brodowisku-blazor-hybrid} Przenosząc skomplikowaną logikę biznesową (tzw. "Heavy Client") z WPF i WinForms, zintegrowane DI staje się fundamentem procesu modernizacji. Strategia ewolucyjna wykorzystująca technologię \textbf{Blazor Hybrid} pozwala na wdrożenie wzorca Generic Host we wnętrzu istniejącego procesu aplikacji desktopowej. \textbf{Wnioski implementacyjne z wdrożenia wstrzykiwania zależności w modelu hybrydowym:}\\ Zamiast przepisywać mechanizmy na nowo, programiści C\# mogą zainicjować instancję \texttt{IServiceCollection} w punkcie wejścia (np. przy uruchamianiu \texttt{App.xaml}). Usługi specyficzne dla sprzętu czy systemu operacyjnego (np. czytnik kodów kreskowych, zapis na dysku C:) mogą zostać zarejestrowane w kontenerze jako Singletony, a nowo stworzone webowe komponenty Razor (osadzone w kontrolce \texttt{BlazorWebView}) zażądają ich przez zwykłe \texttt{@inject}. Dzięki wbudowanemu mechanizmowi Dependency Injection w modelu hybrydowym współdzielenie instancji obiektów między kodem natywnym WPF a nowoczesnym interfejsem staje się bezproblemowe, a ewentualne późniejsze oderwanie logiki do zewnętrznego mikroserwisu odbywa się naturalnie. \section{Zarządzanie Konfiguracją (appsettings.json) w Architekturze Generic Host}\label{zarzux105dzanie-konfiguracjux105-appsettings.json-w-architekturze-generic-host} W klasycznych aplikacjach desktopowych (WPF, WinForms) opartych na .NET Framework 4.8, zarządzanie konfiguracją opierało się niemal wyłącznie na plikach XML, takich jak \texttt{App.config} czy \texttt{Web.config}, do których dostęp uzyskiwano statycznie poprzez klasę \texttt{ConfigurationManager}. W nowoczesnym ekosystemie .NET, fundamentem zarządzania cyklem życia i zasobami jest \textbf{Generic Host}, który centralizuje i unowocześnia proces ładowania ustawień. Zamiast sztywnego formatu XML, standardem stał się format JSON, w szczególności plik \texttt{appsettings.json}, który we współpracy z wbudowanym mechanizmem wstrzykiwania zależności (DI) diametralnie zmienia architekturę dostępu do danych konfiguracyjnych. \paragraph{1. Mapowanie pojęć: Desktop vs Generic Host}\label{mapowanie-pojux119ux107-desktop-vs-generic-host} Przejście na architekturę Generic Host wymaga od programistów desktopowych zmiany paradygmatu w sposobie definiowania i odczytywania parametrów startowych: {\def\LTcaptype{none} % do not increment counter \begin{longtable}[]{@{}lll@{}} \toprule\noalign{} Koncepcja Desktop (WPF/WinForms) & Odpowiednik w .NET (Generic Host) & Opis zmiany architektonicznej \\ \midrule\noalign{} \endhead \bottomrule\noalign{} \endlastfoot \textbf{\texttt{App.config} / \texttt{Web.config} (XML)} & \textbf{\texttt{appsettings.json}} & Format XML został zastąpiony czystą, hierarchiczną strukturą JSON, która ułatwia organizację złożonych ustawień i sekcji. \\ \textbf{Transformacje XML (Slow/Complex)} & \textbf{Konfiguracja środowiskowa} & Host automatycznie wczytuje nadpisujące pliki zależne od środowiska, np. \texttt{appsettings.Development.json} lub \texttt{appsettings.Production.json}. \\ \textbf{\texttt{ConfigurationManager.AppSettings}} & \textbf{Interfejs \texttt{IConfiguration}} & Odczyt statyczny ustępuje miejsca wstrzykiwaniu zależności. Host ładuje ustawienia do scentralizowanego obiektu konfiguracyjnego. \\ \textbf{Pobieranie pojedynczych kluczy} & \textbf{Wzorzec Opcji (Options Pattern)} & Platforma preferuje silnie typowane wiązanie hierarchii JSON do klas języka C\#, co redukuje błędy i upraszcza testowanie. \\ \end{longtable} } \paragraph{2. Architektura systemowa: Host jako konfigurator zasobów}\label{architektura-systemowa-host-jako-konfigurator-zasobuxf3w} W momencie uruchamiania aplikacji, Generic Host (lub jego nowsza inkarnacja \texttt{WebApplicationBuilder}) odpowiada za hermetyzację wszystkich zasobów systemowych, w tym logowania, kontenera DI oraz konfiguracji. Zamiast polegać na jednym pliku, Host buduje konfigurację poprzez agregację danych z uporządkowanego zestawu dostawców. Domyślna konfiguracja tworzona przez metodę \texttt{Build()} wczytuje ustawienia w następującej kolejności: \begin{enumerate} \tightlist \item Plik \texttt{appsettings.json}. \item Pliki środowiskowe (np. \texttt{appsettings.Development.json}). \item Zmienne środowiskowe, które automatycznie nadpisują wartości pobrane z plików JSON. \item Argumenty wiersza poleceń. \end{enumerate} Kluczowym wnioskiem architektonicznym dla systemów klasy Enterprise jest fakt, że pliki \texttt{appsettings.json} służą do przechowywania struktury, ale nie powinny zawierać danych poufnych w postaci jawnego tekstu (takich jak hasła) w środowiskach produkcyjnych. W architekturze zorientowanej na chmurę zaleca się wykorzystanie narzędzia Secret Manager (w fazie deweloperskiej) oraz usług takich jak Azure Key Vault do bezpiecznego wstrzykiwania sekretów. \paragraph{3. Laboratorium kodu: Wzorzec Opcji i wstrzykiwanie konfiguracji}\label{laboratorium-kodu-wzorzec-opcji-i-wstrzykiwanie-konfiguracji} Najlepszą praktyką odczytu konfiguracji w nowoczesnym .NET jest zastosowanie \textbf{wzorca opcji} (Options Pattern), który wiąże struktury JSON z silnie typowanymi klasami. Eliminuje to konieczność używania "magicznych ciągów znaków" (ang. \emph{magic strings}) rozrzuconych po logice biznesowej. \textbf{Analiza przypadku -- Struktura pliku \texttt{appsettings.json}:} \begin{Shaded} \begin{Highlighting}[] \NormalTok{\{} \NormalTok{ "Logging": \{} \NormalTok{ "LogLevel": \{} \NormalTok{ "Default": "Information"} \NormalTok{ \}} \NormalTok{ \},} \NormalTok{ "AppSettings": \{} \NormalTok{ "FeatureToggle": true,} \NormalTok{ "ApplicationName": "ModulBiznesowyWPF",} \NormalTok{ "MaxUsers": 1000} \NormalTok{ \}} \NormalTok{\}} \end{Highlighting} \end{Shaded} Powyższy plik stanowi odpowiednik dawnych sekcji w \texttt{App.config}. Zamiast statycznego \texttt{ConfigurationManager}, definiujemy klasę odzwierciedlającą tę strukturę: \begin{Shaded} \begin{Highlighting}[] \NormalTok{public class AppSettings} \NormalTok{\{} \NormalTok{ public bool FeatureToggle \{ get; set; \}} \NormalTok{ public string ApplicationName \{ get; set; \}} \NormalTok{ public int MaxUsers \{ get; set; \}} \NormalTok{\}} \end{Highlighting} \end{Shaded} \textbf{Rejestracja w kontenerze Hosta (\texttt{Program.cs}):} \begin{Shaded} \begin{Highlighting}[] \NormalTok{var builder = WebApplication.CreateBuilder(args);} \NormalTok{// Rejestracja konfiguracji w kontenerze DI za pomocą wzorca Opcji} \NormalTok{builder.Services.Configure\textless{}AppSettings\textgreater{}(builder.Configuration.GetSection("AppSettings"));} \NormalTok{var app = builder.Build();} \NormalTok{// Uruchomienie aplikacji...} \end{Highlighting} \end{Shaded} \textbf{Wstrzykiwanie do komponentu Blazor lub usługi:}\\ Kontrolery lub usługi biznesowe nie odpytują pliku samodzielnie -- otrzymują gotowy, rzutowany obiekt za pośrednictwem konstruktora. \begin{Shaded} \begin{Highlighting}[] \NormalTok{public class MyBusinessService} \NormalTok{\{} \NormalTok{ private readonly AppSettings \_appSettings;} \NormalTok{ // Interfejs IOptions pozwala wstrzyknąć silnie typowaną konfigurację} \NormalTok{ public MyBusinessService(IOptions\textless{}AppSettings\textgreater{} appSettings)} \NormalTok{ \{} \NormalTok{ \_appSettings = appSettings.Value;} \NormalTok{ \}} \NormalTok{ public void ProcessData()} \NormalTok{ \{} \NormalTok{ Console.WriteLine($"Aplikacja: \{\_appSettings.ApplicationName\}");} \NormalTok{ \}} \NormalTok{\}} \end{Highlighting} \end{Shaded} \paragraph{4. Strategia Migracji: Scenariusze Non-Web i Blazor Hybrid}\label{strategia-migracji-scenariusze-non-web-i-blazor-hybrid} Dla deweloperów planujących migrację złożonych aplikacji WPF, pełne przepisanie logiki odczytu \texttt{App.config} wydaje się ryzykownym procesem. Jednakże architektura Generic Host wspiera również \textbf{scenariusze nie-webowe (non-web scenarios)}. \textbf{Wnioski implementacyjne dla strategii hybrydowej:}\\ Programiści C\# mogą zainicjować instancję Generic Host w klasie \texttt{App.xaml.cs} swojej istniejącej aplikacji WPF. Pozwala to natychmiastowo zrezygnować z przestarzałego \texttt{ConfigurationManager} na rzecz nowoczesnego \texttt{appsettings.json} oraz silnie typowanego wzorca opcji. Tak przygotowany Host zarządza cyklem życia nie tylko starych usług natywnych, ale jest też perfekcyjnie zintegrowany z kontrolką \texttt{BlazorWebView}, używaną w podejściu \textbf{Blazor Hybrid}. Komponenty Razor osadzone w okienkach WPF zyskują w ten sposób dokładnie taki sam i ustandaryzowany dostęp do ustawień (\texttt{@inject\ IOptions\textless{}T\textgreater{}}), jak miałyby, gdyby aplikacja działała jako czysty wariant Blazor WebAssembly czy Blazor Server w chmurze. \end{document}