diff --git a/docs/ReC.Client.xwiki b/docs/ReC.Client.xwiki index 059cd78..53a62db 100644 --- a/docs/ReC.Client.xwiki +++ b/docs/ReC.Client.xwiki @@ -367,31 +367,40 @@ Empfehlungen: * Verwenden Sie `Assert.ThrowsAsync(...)`, um Fehlerpfade zu prüfen, und werten Sie `StatusCode`, `Method` und `RequestUri` aus. * Für GET-Tests reicht eine einzelne Methode pro Verhalten (typisiert vs. dynamisch) statt redundanter Setups. -== 6. Veraltete / statische APIs == +== 6. Komfort-APIs: statischer Provider und synchrone Wrapper == -Einige historische Hilfsmittel sind weiterhin vorhanden und funktionsfähig, jedoch mit `[Obsolete]` markiert. Sie sind kein Breaking Change – Sie können sie übergangsweise weiter benutzen, sollten aber langfristig migrieren. +Neben dem empfohlenen DI-basierten Ansatz bietet **ReC.Client** absichtlich auch einen **statischen Komfort-Pfad** sowie **synchrone Wrapper** an. Diese Bestandteile sind nicht „veraltet im Sinne von eingefroren" – sie werden weiterhin gepflegt und bei Bedarf um neue Funktionen erweitert (Beispiel: der jüngst hinzugefügte optionale `Action`-Parameter in `BuildStaticClient`). Sie sind jedoch bewusst mit `[Obsolete]` markiert, damit Aufrufer sie nicht „aus Versehen" auswählen, sondern eine bewusste Entscheidung treffen. -Betroffen: +Hintergrund -* `ReCClient.BuildStaticClient(string)` und `ReCClient.BuildStaticClient(Action)` -* `ReCClient.Create()` -* `TaskSyncExtensions.Sync(...)` und `TaskSyncExtensions.Sync(...)` +* In Projekten, die noch auf **.NET Framework 4.6.2** basieren, ist `Microsoft.Extensions.DependencyInjection` häufig nicht etabliert und die Einführung einer DI-Infrastruktur kostet Zeit. Damit Entwickler dort nicht ins Stocken geraten, gibt es einen statischen Einstieg, der **sofort einsatzbereit** ist. +* Synchroner Code in älteren Codebasen kann nicht überall sofort auf `async/await` umgestellt werden. Für solche Stellen existieren die `Sync()`-Erweiterungen als pragmatische Brücke. -Warum als `Obsolete` markiert? +Status -* **Statischer Provider**: Ein global gehaltener `IServiceProvider` erschwert die Verwaltung von `HttpClient`-Lebenszyklen, kann zu schwer reproduzierbaren Problemen in lang laufenden Prozessen führen und behindert Tests. -* **Synchrone Blockade**: `Task.GetAwaiter().GetResult()` (was `TaskSyncExtensions.Sync` intern tut) kann in Umgebungen mit `SynchronizationContext` (z. B. WinForms, WPF, einige Test-Runner) zu Deadlocks führen und macht Fehler schwerer diagnostizierbar. +* **Aktiv gepflegt**: Beide Pfade erhalten weiterhin Funktionalitäts- und Komfort-Updates. +* **`[Obsolete]`-Markierung als Erinnerung**: Die Compiler-Warnung soll bewusst sichtbar bleiben, damit Teams die Übergangslösung nicht vergessen und mittelfristig zu DI + `async/await` migrieren. +* **Kein Breaking-Change-Risiko**: Aufrufe bleiben kompilierbar und ausführbar. -Funktionieren sie noch? +Wann der statische Pfad sinnvoll ist -* Ja. Die Methoden bleiben kompilierbar und ausführbar. Sie erhalten lediglich eine Compiler-Warnung bei der Verwendung. +* Bestehender VB.NET-/C#-Code auf .NET Framework 4.6.2 ohne eigene DI-Infrastruktur. +* Kleine Werkzeuge, Skripte oder Konsolenanwendungen, bei denen ein vollständiges Host-Setup übertrieben wäre. +* Schneller Einstieg in die Bibliothek, um ein erstes Ergebnis zu sehen, bevor die endgültige Architektur entschieden wird. -Wann darf man sie übergangsweise nutzen? +Wann besser DI verwenden -* In Legacy-Code, der nicht sofort auf DI + `async/await` umgestellt werden kann. -* In sehr kurzen, isolierten Skripten oder Konsolenanwendungen ohne `SynchronizationContext`, wo das Risiko überschaubar ist. +* Lang laufende Prozesse, Serveranwendungen, Tests. +* Sobald in der Anwendung ohnehin `IServiceCollection` / `IHostBuilder` vorhanden ist. +* Wenn `HttpClient`-Lebenszyklen, Logging-Scopes oder Optionen sauber verwaltet werden sollen. -Beispiel – statischer Client (nicht empfohlen, aber möglich): +=== 6.1 Statischer Client mit BuildStaticClient / Create === + +`ReCClient.BuildStaticClient(...)` baut intern eine `IServiceCollection` auf, ruft `AddRecClient(...)` auf und legt einen statischen `IServiceProvider` ab. Anschließend liefert `ReCClient.Create()` Instanzen aus diesem Provider. + +Wichtig: `BuildStaticClient` darf **nur einmal** beim Anwendungsstart aufgerufen werden. + +Variante mit Basis-URL: {{code language="vb.net"}} ' Einmalig beim Anwendungsstart @@ -411,23 +420,64 @@ var client = ReCClient.Create(); await client.RecActions.InvokeAsync(profileId, "batch-001"); {{/code}} -Beispiel – synchrone Blockade (nicht empfohlen, aber möglich): +Variante mit `HttpClient`-Konfiguration: + +{{code language="vb.net"}} +ReCClient.BuildStaticClient(Sub(http) + http.BaseAddress = New Uri("https://ihre-rec-api-adresse.com/") + http.Timeout = TimeSpan.FromSeconds(30) +End Sub) +{{/code}} + +{{code language="csharp"}} +ReCClient.BuildStaticClient(http => +{ + http.BaseAddress = new Uri("https://ihre-rec-api-adresse.com/"); + http.Timeout = TimeSpan.FromSeconds(30); +}); +{{/code}} + +Optional kann zusätzlich `ReCClientOptions` über einen Callback gesetzt werden – die Signatur entspricht der von `AddRecClient`: + +{{code language="vb.net"}} +ReCClient.BuildStaticClient( + "https://ihre-rec-api-adresse.com/", + Sub(opt) + opt.LogSuccessfulRequests = True + End Sub) +{{/code}} + +{{code language="csharp"}} +ReCClient.BuildStaticClient( + "https://ihre-rec-api-adresse.com/", + opt => + { + opt.LogSuccessfulRequests = true; + }); +{{/code}} + +=== 6.2 Synchrone Wrapper über TaskSyncExtensions === + +`TaskSyncExtensions.Sync()` bzw. `Sync()` blockieren den aktuellen Thread, bis die `Task` abgeschlossen ist. Sie sind nützlich, wenn der umliegende Code (noch) nicht asynchron sein kann. {{code language="vb.net"}} Imports ReC.Client -' Achtung: kann zu Deadlocks führen +' Blockiert bis die Task fertig ist recClient.RecActions.InvokeAsync(profilId, "batch-001").Sync() {{/code}} {{code language="csharp"}} using ReC.Client; -// Warning: may deadlock depending on context +// Blocks until the task completes recClient.RecActions.InvokeAsync(profileId, "batch-001").Sync(); {{/code}} -Migrations-Tipps: +Hinweis: In Umgebungen mit einem `SynchronizationContext` (z. B. WinForms, WPF oder bestimmte Test-Runner) kann blockierendes Warten zu Deadlocks führen. Für Konsolen- und Hintergrundprozesse ist das Risiko in der Regel gering. Wo immer möglich: `async/await` bevorzugen. -* **`BuildStaticClient` / `Create`** ? ersetzen durch `services.AddRecClient(...)` und Konstruktor-Injektion. -* **`TaskSyncExtensions.Sync`** ? den umliegenden Codepfad asynchron machen (`async Task`) und `await` verwenden. +=== 6.3 Mittelfristige Empfehlung === + +* **`BuildStaticClient` / `Create`**: für Legacy-Einstiege okay; sobald `IServiceCollection` vorhanden ist, auf `services.AddRecClient(...)` und Konstruktor-Injektion umstellen. +* **`TaskSyncExtensions.Sync`**: nur lokal kapseln. Wenn eine Methode bereits `async` sein darf, direkt `await` verwenden. +* Die `[Obsolete]`-Warnung dient als **dauerhafter Reminder**, dass es sich um einen bewussten Komfort-Pfad handelt – sie ist kein Hinweis darauf, dass die APIs entfernt werden.