Blazor WebAssembly ist endlich erschienen

Zehn Unterschiede zu Blazor Server
11
Aug

Blazor WebAssembly ist endlich erschienen

Blazor WebAssembly ist am 19. Mai 2020 erstmals in einer stabilen Version erschienen. In diesem Beitrag stelle ich kurz zehn Gemeinsamkeiten und ausführlich zehn Unterschiede zu dem bereits im September 2019 erschienenen Blazor Server vor.

Blazor WebAssembly und Blazor Server sind sich grundsätzlich sehr ähnlich:

  1. Beide Architekturen basieren auf .NET. Der Webentwickler schreibt C#, HTML, CSS und in einigen Fällen etwas JavaScript.
  2. Nicht auf alle Funktionen eines Webbrowsers kann man in Blazor direkt per C#-Code zugreifen. Derzeit müssen Entwickler selbst für vermeintlich einfache Funktionen auf die Interoperabilität mit JavaScript zurückgreifen, z. B. für das Lesen und Schreiben von Cookies und anderen lokalen Speichertypen des Webbrowsers. Die Interoperabilität funktioniert jeweils in beide Richtungen: C#-Code kann JavaScript und JavaScript wieder C#-Code aufrufen.
  3. Seiten und Seitenteile definiert man in Razor Components. Diese bestehen wahlweise nur aus einem Razor Template (.razor mit C#-Einschüben nach dem @-Zeichen), aus einem Razor Template plus Code-Behind-Datei in C# oder nur aus einer C#-Klasse. Razor Components sind jedoch keine Web Components gemäß W3C-Standard, sondern nur Komponenten innerhalb von Blazor.
  4. Razor Components sind zustandsbehaftet, d. h., die Werte in Klassenmitgliedern (Fields und Properties) bleiben erhalten, solange die Komponente aktiv (sichtbar) ist.
  5. Razor Components feuern Lebenszyklusereignisse (OnInitialized(), OnAfterRender() usw.) und Benutzerereignisse (@onclick, @onkeyup usw.), auf die der Entwickler im C#-Code reagiert.
  6. Die Razor Components manipulieren ein Virtual Document Object Model (DOM), Blazor synchronisiert es mit dem echtem DOM des Browsers.
  7. Die .NET Core Dependency Injection kommt zum Einsatz (z. B. für den NavigationManager zum Abruf und zur Steuerung des Browser-URL).
  8. Eine Kapselung von Razor Components, C#-Code, JavaScript-Code und statischen Inhalten ist in Razor Class Libraries (DLLs) möglich. Es gibt bereits zahlreiche hilfreiche Razor-Class-Library-Pakete aus der Community. Ein Verzeichnis findet man auf GitHub unter „Awesome Blazor“.
  9. Es gibt in beiden Blazor-Varianten noch kein Konzept für zur Laufzeit nachladbare Module (Lazy Loading), was bedeutet, dass alle Razor Components einer Anwendung bereits beim Anwendungsstart geladen werden müssen.
  10. Die Authentifizierung erfolgt über eine Ableitung der Klasse Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider. Es gibt eine optionale Integration mit ASP.NET Core Identity.

Das waren in Kürze zehn Gemeinsamkeiten, die dazu beitragen, dass man sehr leicht Programmcode zwischen Blazor WebAssembly und Blazor Server austauschen bzw. gemeinsamen Code und gemeinsame Oberflächen in beiden Architekturen verwenden kann.

Ein interessanter Ansatz ist, eine Webanwendung so in eine Razor Class Library zu kapseln, dass sie sowohl in Blazor WebAssembly als auch Blazor Server verwendet werden kann. Man besitzt dann zwei Kopf-/Start-Projekte und gemeinsame DLLs mit der Oberfläche, dem Programmcode und Ressourcen, wie man es heute schon von Xamarin.Forms kennt.

Im Folgenden wird dieser Beitrag zehn Unterschiede genauer erläutern, die in der Praxis zu beachten sind, wenn man sich für eine der beiden Architekturen entscheiden oder gemeinsam nutzbare Razor Class Libraries schaffen will.

Verschaffen Sie sich den Zugang zur .NET- und Microsoft-Welt mit unserem kostenlosen Newsletter!

1. Versionsnummer und Support

Die aktuelle Veröffentlichung von Blazor Server trägt die Versionsnummer 3.1.5. Bei Blazor WebAssembly ist die erste Version direkt unter der Versionsnummer 3.2 erschienen; Versionen 1.0 bis 3.1 hat es nie gegeben. Der Unterschied zwischen den Versionsnummern 3.1 bei Blazor Server zu 3.2 bei Blazor WebAssembly bedeutet aber keineswegs, dass Blazor WebAssembly der Nachfolger von Blazor Server ist. Die Versionsnummer 3.2 bedeutet nur, dass die erste Version von Blazor WebAssembly außerhalb des Veröffentlichungszyklus von .NET Core erschienen ist. Die nächste Version wird 5.0 sein. Diese Versionsnummer wird sowohl für Blazor Server als auch Blazor WebAssembly gelten und am 10. November 2020 im Rahmen der .NET Conf 2020 als Teil von .NET 5.0 erscheinen. Auf GitHub findet man eine Roadmap für geplante Verbesserungen und weitere Funktionen in ASP.NET Core Blazor 5.0, die aber wahrscheinlich nicht alle schon in .NET 5.0 erscheinen werden.

Blazor in ASP.NET Core 3.1 ist eine Long-Term-Support-Version (LTS), die Microsoft drei Jahre nach dem Erscheinen mit Bugfixes und Sicherheitsupdates sowie mit kommerziellem Support unterstützt. Die Version 3.1 ist am 3. Dezember 2019 erschienen und wird also bis zum 3. Dezember 2022 unterstützt. Blazor WebAssembly 3.2 hat allerdings lediglich den Status Current Version, erhält also nicht den Long-Term-Support der .NET-Core-Version 3.1 aus dem Dezember 2019. Das bedeutet, dass Anwender nach dem Erscheinen der nächsten Version am 10. November 2020 nur drei Monate Zeit haben werden, die Blazor-WebAssembly-Version zu wechseln. Die erste Long-Term-Support-Version von Blazor WebAssembly wird nach aktuellem Wissen erst die Version 6.0 in .NET 6.0 im November 2021 sein.

2. Architektur

Bei Blazor Server (früher: Server-side Blazor) läuft der .NET-Programmcode auf dem Webserver. Die HTML-Oberfläche wird auf dem Webserver in einem sogenannten Virtual DOM gerendert und mit einem kompakten Synchronisierungsverfahren in einzelnen Datenpaketen über eine kontinuierliche WebSocket-Verbindung zum Client übertragen. Eine JavaScript-Bibliothek im Browser aktualisiert mit diesen Datenpaketen die Oberfläche im Sinne eines Remote Renderings.

Bei Blazor WebAssembly (früher: Client-side Blazor) laufen der .NET-Programmcode und das HTML-Rendering tatsächlich im Webbrowser in dessen WebAssembly-Laufzeitumgebung. Eine JavaScript-Bibliothek kommt dennoch auch hier zum Einsatz, um zwischen der WebAssembly Virtual Machine des Browsers und dem DOM zu synchronisieren, denn die WebAssembly VM des Browsers hat laut W3C-Standard keinen Zugriff auf dessen DOM.

 

Abb. 1: Blazor Server vs. Blazor WebAssembly

 

Der Benutzer der Webanwendung erlebt in beiden Fällen eine Single Page Application (SPA). Allerdings kann eine Blazor-Server-basierte Webanwendung niemals offlinefähig werden (daher in Abbildung 1 „SPA light“ genannt), und es gibt auch Probleme bei nicht konstanten Netzwerkverbindungen: Wenn der Webserver nicht mehr erreichbar ist, wird die aktuelle Ansicht im Browser ausgegraut und der Benutzer sieht oben die Warnmeldung „Attempting to reconnect to the server …“. Sollte das nicht gelingen, erfolgt dann die Meldung „Could not reconnect to the server. Reload the page to restore functionality.“ Wenn der Benutzer dann nicht wieder am Anfang der Anwendung landen soll, sondern an seinem letzten Standort, müssen Zustände im Browser gehalten werden.

Blazor Server hat zudem Herausforderungen bei der Skalierbarkeit, denn das Virtual DOM und der Zustand aller Razor Components liegt für jeden aktuell angeschlossenen Browser im RAM des Webservers. Microsoft hat dokumentiert, dass für jeden angeschlossenen Browser schon bei einer Hello-World-Anwendung 250 KB RAM im Server gebraucht werden. Für echte Anwendungen ist der RAM-Bedarf jeweils selbst zu messen, weil auch Microsoft keine Formel besitzt, um ihn vorherzusagen. Dass Microsoft das nicht für eine beliebige Anwendung vorhersagen kann, ist nachvollziehbar, denn der RAM-Bedarf ist von vielen Faktoren abhängig, insbesondere natürlich von der Komplexität der Benutzeroberflächen und der Menge der im Zustand der Komponente gehaltenen Daten. Im Rahmen der .NET Conf am 14.01.2020 hat Microsoft eigene Messergebnisse veröffentlicht. So kann auf einer virtuellen Maschine in der Azure-Cloud mit 4 virtuellen CPUs und 14 GB RAM bei 20 000 gleichzeitigen Benutzern immer noch eine Latenz (Verzögerung bei der Darstellung von Änderungen) von unter 200 Millisekunden erreicht werden.

3. Hosting

Während eine Blazor-Server-Anwendung naturgemäß immer auf einem ASP.NET-Core-fähigen Webserver laufen muss, gibt es bei Blazor WebAssembly zwei alternative Hosting-Optionen:

  • Hosting auf einem ASP.NET-Core-fähigen Webserver
  • Hosting auf einem beliebigen anderen Webserver, der nur statische Inhalte ausliefern können muss

Bei der Auswahl der Projektvorlage Blazor WebAssembly in Visual Studio bzw. bei dotnet new blazorwasm gibt es daher noch die Zusatzoption ASP.NET Core hosted (auf der Kommandozeile: –hosted). Ohne diese Option wird eine rein lokal im Browser laufende Blazor-WebAssembly-Anwendung erstellt. Für die Installation einer Blazor-WebAssembly-Anwendung muss das IIS-Zusatzmodul URL Rewrite auf dem Webserversystem installiert werden. Nur wenn die Blazor-WebAssembly-Anwendung auch noch ein ASP.NET-Core-basiertes WebAPI-Projekt umfasst, muss auch das ASP.NET Core Runtime Hosting Bundle installiert sein.

Ein Hosting in Azure App Services ist sowohl für Blazor-Server- als auch Blazor-WebAssembly-Anwendungen möglich. Eine Blazor-WebAssembly-Anwendung kann allerdings derzeit nur in einem Windows-basierten Azure-App-Service verwendet werden („A Linux server image to host the app isn’t available at this time.“). Blazor Server kann auch in Linux-basierten App Services laufen.

Während Blazor Server komplett auf .NET Core basiert und auch Blazor-WebAssembly-Projekte mit dem .NET Core SDK übersetzt werden, wirken bei Blazor WebAssembly zur Laufzeit eine Mono-basierte Laufzeitumgebung (aktuell: Mono 6.13.0) und das Projekt Emscripten. Ein weiterer Unterschied zwischen Blazor Server und Blazor WebAssembly ist, dass bei Ersterem beim Deployment eine .exe-Datei aus dem Projekt entsteht, beim zweiten eine .dll-Datei. Bei Blazor Server ist also ein Selfhosting außerhalb eines Webservers möglich, wie bei ASP.NET Core üblich, indem man die entstandene .exe-Datei einfach startet. Hier startet der in ASP.NET integrierte Webserver Kestrel.

4. Projektaufbau

Der Projektaufbau bei Blazor Server und Blazor WebAssembly ist ähnlich (Verzeichnisse /wwwroot, /Pages und /Shared, Dateien App.razor und launchsettings.json), aber im Detail gibt es Unterschiede. Für Blazor WebAssembly gibt es zwei Projektvorlagen: für den eigenständigen Fall und für den ASP.NET-Core-gehosteten (Abb. 2). Bei Blazor Server und der eigenständigen Blazor-WebAssembly-Anwendung entsteht jeweils nur ein Projekt. Mit der Option ASP.NET Core hosted entstehen gleich drei Projekte:

  • Name.Client ist die Blazor-WebAssembly-basierte Frontend-Webanwendung für den Webbrowser.
  • Name.Server ist ein ASP.NET-Core-basiertes Backend, das einerseits das Blazor-WebAssembly-basierte Frontend lädt, andererseits ein Web API bereitstellt, das Daten an das Frontend liefert.
  • Name.Shared ist eine .NET-Standard-Bibliothek, die sowohl in Client als auch Server eingebunden wird. Sie stellt gemeinsame Geschäftsobjekte (WeatherForecast.cs) bereit.

Der Startcode einer Blazor-WebAssembly-Anwendung besteht seit Version 3.2 Preview 1 vom 28.01.2020 nur noch aus einer Klasse: Program.cs. Hier wird auch die .NET Core Dependency Injection (Microsoft.Extensions.DependencyInjection) konfiguriert. Die aus ASP.NET-Core-Projekten bekannte und auch in Blazor Server verwendete Zweiteilung in Program.cs und Startup.cs ist entfallen. Die Rahmenseite mit dem – und -Tag ist bei Blazor WebAssembly die statische Datei wwwroot/index.html, bei Blazor Server ist es eine Razor Page: pages/_host.cshtml.

 

Abb. 2: Projektaufbau bei Blazor Server vs. Blazor WebAssembly

 

5. Debugging

Debugging ist in Blazor WebAssembly trotz der komplexen Softwarearchitektur zwar inzwischen grundsätzlich möglich, aber nicht alle aus Visual Studio bekannten .NET-Debugging-Funktionen sind implementiert. Möglich beim Debugging von Blazor-WebAssembly-Anwendungen ist Folgendes:

  • Haltepunkte in Razor Components (im Template und im Code-Behind) sowie davon aufgerufenem Code
  • Variablenwerte können durch die Tooltips sowie die Fenster Locals und Watch betrachtet werden
  • Debuggersteuerung mit F10 (Step Over), F11 (Step Into) sowie F5 (Fortsetzen)
  • Ansicht des Call-Stacks

Das Folgende ist leider bisher nicht bzw. nur eingeschränkt möglich:

  • Debugging mit Browsern, die nicht auf Chromium basieren, d. h. Debugging ist derzeit nur mit Google Chrome und dem neuen Microsoft Edge (ab Version 79) möglich
  • Haltepunkte in der Startphase der Anwendung (also in Program.cs)
  • Aufruf des Debuggers bei unbehandelten Ausnahmen (Exceptions)
  • Aufruf des Debuggers mit System.Diagnostics.Debugger.Break()
  • Anzeige der Inhalte von einigen Objektmengen (z. B. Dictionaries)
  • Nutzung des Auto-Fensters
  • Das Call Stack-Fenster zeigt als Sprache auch für C#-Code immer „JavaScript“ an
  • Im Immediate Window funktionieren viele Zugriffe nicht, z. B. auf Listeninhalte

Bei Blazor Server sind hingegen alle Debugging-Funktionen von Visual Studio uneingeschränkt verfügbar.

Blazor WebAssembly kann neben dem Debugging in Visual Studio auch ein Debugging direkt im Chromium-basierten Browser vollziehen. Dazu drückt man bei einer lokal gestarteten Blazor-WebAssembly-Anwendung die Tastenkombination SHIFT + ALT + D und folgt dann der Anweisung, um den Browser im Debug-Modus zu starten.

6. Anwendungsgröße und Startzeiten

Blazor WebAssembly lädt beim Start eine Vielzahl von Dateien, der Anwendungsstart ist entsprechend langsam. Selbst eine minimale Blazor-WebAssembly-Anwendung mit einer einzigen statischen Razor Component benötigt beim Start 35 HTTP-Anfragen mit insgesamt 5,5 MB Netzwerkdatenverkehr (Abb. 3) nach einem Deployment im Releasemodus unter Einsatz des Mono-IL-Linkers und Brotli-Komprimierung. Wenn man in den Entwicklerwerkzeugen im Chrome-Browser eine „Fast-3G“-Verbindung simulieren lässt, sieht der Benutzer rund 32 Sekunden lang „Loading …“. Das ist inakzeptabel für viele Internetanwendungen, man kann es allenfalls bei Intra- und Extranetanwendungen vertreten. Bei aktiviertem Cache dauert das zweite Laden rund 6,5 Sekunden.

Immerhin wächst die Anwendungsgröße nicht linear. Eine Blazor-WebAssembly-Anwendung aus der Praxis mit rund 30 Komponenten umfasst 7,5 MB (Abb. 4; geladen von einem Azure-App-Service in den USA), mit 200 Komponenten 8,7 MB (Abb. 5; geladen im lokalen Netzwerk). Die Anzahl der HTTP-Anfragen ändert sich nicht bei steigender Komponentenanzahl, da alle Razor Components eines Projekts in eine einzige DLL hineinkompiliert werden. In Abbildung 4 sieht man aber bei der Praxisanwendung mehr HTTP-Anfragen, da hier viele weitere Ressourcen (Style Sheets, Grafiken, JavaScript-Dateien etc.) verwendet werden.

 

Abb. 3: Das „Hello-World“-Kompilat in Blazor WebAssembly

 

Abb. 4: Eine Blazor-WebAssembly-Anwendung mit 30 Komponenten

 

Abb. 5: Eine größere Blazor-WebAssembly-Anwendung mit 200 Komponenten

 

7. Laufzeitverhalten

Bei Blazor Server findet die in .NET übliche Übersetzung von Intermediate Language (MSIL) in Maschinencode per Just-in-Time-(JIT-)Compiler statt. Bei Blazor WebAssembly wird ebenfalls MSIL in den Browser geladen, dort aber von einem MSIL-Interpreter innerhalb der dortigen .NET-Laufzeitumgebung interpretiert, die eine WebAssembly-basierte Variante von Mono ist. Diese Interpreterarchitektur macht schon deutlich, dass die Codeausführung in Blazor WebAssembly deutlich langsamer als bei Blazor Server ist. Daniel Roth, Program Manager im ASP.NET-Team bei Microsoft, bestätigt das in der Antwort auf eine Frage in seinem Blogeintrag: „Blazor WebAssembly runs on a .NET IL interpreter based runtime – there’s no JIT compilation to WebAssembly happening, so execution performance is much slower than normal .NET code execution.“ In seiner Antwort auf die Performancefrage des Kunden geht er auch offen damit um, dass Blazor WebAssembly derzeit hinsichtlich der Ausführungsgeschwindigkeit nicht konkurrenzfähig zu aktuellen JavaScript Frameworks ist: „Blazor WebAssembly isn’t going to win in any performance comparisons with JavaScript based frameworks like Angular or React.“

Wie aufwendig die Verarbeitung bei Blazor WebAssembly ist, zeigt sich bei Leistungsmessungen: Eine Reihe von 100 000 Fibonacci-Berechnungen, die in Blazor Server inklusive Übertragung von Parametern und Ergebnis zwischen den Prozessen 373 Millisekunden braucht, dauert bei der Ausführung in Blazor WebAssembly in Chrome stattliche 10 Sekunden (Abb. 6). Damit man nicht Äpfel mit Birnen vergleicht, ist hier der Webserver der gleiche PC, auf dem auch der Browser läuft. Bei diesem gewaltigen Unterschied (Faktor 38) darf aber man aber nicht vergessen, dass sich in der Praxis bei Blazor Server alle aktiven Benutzer die Rechenleistung des Webservers teilen, während Blazor WebAssembly die CPU jedes Clients ausnutzt.

Schon bei der Erstankündigung von Blazor im März 2018 brachte Microsoft auch eine Ahead-of-Time-Kompilierung (AOT) von MSIL in WebAssembly-Bytecode ins Spiel, die die Ausführung beschleunigen könnte. AOT ist aber nicht in der jetzt erschienenen Version 3.2 enthalten. Microsoft tut sich seit Jahren schwer mit AOT für .NET. Man hat schon diverse Ansätze versucht und wieder beerdigt.

Die Leistungsdaten in Abbildung 6 wurden jeweils mit den Webbrowsern Chrome und Firefox auf drei Servertypen erhoben:

  1. auf einem lokalen System (Webbrowser und Webserver auf dem gleichen Rechner, ein aktueller Entwickler-PC mit AMD Ryzen 9 3950X mit 16 Kernen und 128 GB RAM)
  2. in der Microsoft-Azure-Cloud als Web-App-Service-gehostete Webanwendungen (Tarif S1, ca. 65 Euro/Monat)
  3. in der Microsoft-Azure-Cloud als Web-App-Service-gehostete Webanwendungen (Tarif P3V2, ca. 500 Euro/Monat)

Abbildung 6 liefert zwei weitere Erkenntnisse:

  • Blazor WebAssembly stürzt bei sehr vielen Rechenoperationen mit dem Laufzeitfehler „System.OutOfMemoryException: Out of memory“ ab, weil die Berechnung die Ergebnisse im RAM speichert und der von der WebAssembly VM nutzbare Speicher begrenzt ist. Während die Dokumentation sich dazu ausschweigt, ergaben meine Tests, dass Blazor WebAssembly dem Entwickler etwas weniger als 1 GB RAM zur freien Verfügung stellt.
  • Bei Blazor Server kann man – wie zu erwarten – durch einen besser ausgestatteten Webserver eine höhere Rechenleistung erreichen.

 

Abb. 6: Leistungsvergleich Blazor Server zu Blazor WebAssembly

 

8. JavaScript-Interoperabilität

Die Interoperabilität zwischen dem C#-Programmcode und JavaScript ist in beiden Blazor-Varianten vorhanden, es gibt jedoch vier Unterschiede:

  • Zentrale Anlaufstelle für die Interoperabilität von .NET mit JavaScript ist die .NET-Schnittstelle Microsoft.JSInterop.IJSRuntime mit den Methoden InvokeAsync() und InvokeVoidAsync() in jeweils drei Überladungen. Der Entwickler muss sich eine Implementierung zu dieser Schnittstelle per Dependency Injection liefern lassen. Unter Blazor Server erhält er eine Instanz der Klasse Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSRuntime, unter Blazor WebAssembly eine Instanz der Klasse Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime. Die verschiedenen Implementierungen ergeben Sinn, denn bei Blazor WebAssembly muss lediglich zwischen der WebAssembly VM und der JavaScript-Laufzeitumgebung innerhalb des Webbrowsers vermittelt werden. Bei Blazor Server liegt das Netzwerk dazwischen: Die Aufrufe mit Parametern und die Rückgabewerte werden über ASP.NET SignalR serialisiert.
  • In Blazor Server ist JavaScript-Interoperabilität allerdings nicht im Rahmen der sogenannten Pre-Rendering-Phase einer Razor Component möglich, d. h. nicht in den Lebenszyklusereignisbehandlungsroutinen OnInitialized() und OnInitializedAsync(), sondern erst in OnAfterRenderAsync(). Wenn man das nicht beachtet, bricht die Blazor-Server-Anwendung zur Laufzeit mit diesem Fehler ab: „JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.“
  • Ein Zugriff auf den Titel des Browserfensters oder andere Daten im -Element der Webseite ist in Blazor WebAssembly nur über JavaScript möglich. Unter Blazor Server kann der Entwickler über die Razor Page _host.cshtml, die im gleichen Prozess läuft wie die Razor Component von Blazor Server, darauf zugreifen.
  • Unter Blazor WebAssembly erzeugt der Aufruf der Methode Console.WriteLine() eine Ausgabe in der Entwicklerkonsole des Webbrowsers. Unter Blazor Server landet Console.WriteLine() auf dem Webserver. Für die Ausgabe auf der Entwicklerkonsole des Webbrowsers muss man per JavaScript-Interoperabilität die JavaScript-Funktion console.log() aufrufen.

API-Zugriffe

Blazor Server kann alle in .NET Core erreichbaren Programmierschnittstellen verwenden, also sowohl .NET Assemblies und COM-Komponenten als auch direkt die Windows-Betriebssystem-APIs Windows 32 (Win32) und Windows Runtime (WinRT). So sind aus einer Blazor-Server-Anwendung heraus zum Beispiel direkte Zugriffe auf Datenbankmanagementsysteme und Verzeichnisdienste sowie an den Server angeschlossene oder im Netzwerk erreichbare Hardware möglich.

Die WebAssembly VM, auf der Blazor WebAssembly aufsetzt, läuft hingegen genau wie JavaScript in der Sandbox des Webbrowsers. Daher kann der Entwickler nicht alle Programmierschnittstellen nutzen. So ist ein direkter Datenbankzugriff in Blazor WebAssembly nicht möglich. Der Entwickler kann zwar einen Datenbankprovider wie Microsoft.Data.SqlClient via NuGet-Paket in den Browser laden, ein Datenbankverbindungsaufbau scheitert dann aber mit dem Laufzeitfehler „Microsoft.Data.SqlClient is not supported on this platform“. Auch andere Netzwerkprotokolle wie LDAP werden damit unterbunden. Der Entwickler muss alle Daten über HTTP-basierte Web Services beziehen und senden. Auch Zugriffe auf viele lokale Ressourcen werden abgeblockt; so kann Blazor WebAssembly nicht via System.Diagnostics.Process.GetCurrentProcess() Informationen über den eigenen Prozess beziehen. Das führt zum Laufzeitfehler „Process has exited or is inaccessible, so the requested information is not available.“ Die Einschränkungen durch die Sandbox sind bei WebAssembly die gleichen wie bei JavaScript.

Bei Blazor WebAssembly ist hingegen der Zugriff auf Server über HTTP-/HTTPS-basierte Web Services (z. B. Web APIs) zu kapseln. Für den Zugriff auf REST-Dienste verwendet man in Blazor die in .NET und .NET Core etablierte Bibliothek System.Net.Http.HttpClient. Sie gehört zum Standardumfang des .NET Core SDK und muss daher in Blazor-Projekten nicht explizit als NuGet-Paket referenziert werden. Optional kann man aber als neue Hilfsbibliothek System.Net.Http.Json verwenden, deren Erweiterungsmethoden Microsofts neuen JSON Serializer System.Text.Json in Verbindung mit HttpClient verwendet. Das Paket System.Net.Http.Json ist in den Blazor-WebAssembly-Projektvorlagen im Standard referenziert, in Blazor Server aber nicht. Die Standardprojektvorlage für Blazor Server enthält kein Beispiel für den Einsatz von HttpClient, da hierbei ja ein Web API nicht zwingend notwendig ist. Wenn man bei der Blazor-WebAssembly-Vorlage die Option ASP.NET Core hosted auswählt, bekommt man ein Serverprojekt mit einem Web-API-Dienst (/Controllers/WeatherForecastController.cs) und im Clientprojekt einen Web-API-Client (/Pages/FetchData.razor). Auch die Nutzung von Diensten, die auf Google RPC (gRPC) basieren, ist in beiden Blazor-Varianten möglich. Ebenso kann ASP.NET Core SignalR eingesetzt werden.

10. PWA-Unterstützung

Die Option in den Projektvorlagen, eine Webanwendung als installierbare Progressive Web App (PWA) zu betreiben (Abb. 7), gibt es nur bei Blazor WebAssembly. Bereits beim Anlegen eines Blazor-WebAssembly-Projekts kann man ein Häkchen setzen (Abb. 8), das alle notwendigen Voraussetzungen schafft. Bei Blazor Server muss man selbst Hand anlegen.

 

Abb. 7: Diese Blazor-WebAssembly-Anwendung mit PWA-Unterstützung ist installierbar

 

Abb. 8: Anlegen einer Blazor-WebAssembly-Anwendung als PWA

 

Es entstehen dadurch, zusätzlich zu den Elementen, die man in Abbildung 2 in Spalte 2 und 3 sieht, weitere Elemente:

  1. Datei /wwwroot/manifest.json (Manifestdatei)
  2. Datei /wwwroot/service-worker.js
  3. Datei /wwwroot/service-worker.published.js
  4. Datei /wwwroot/icon-512.png
  5. Zeile <link href=“manifest.json“ rel=“manifest“ /> in index.html
  6. Zeile <link rel=“apple-touch-icon“ sizes=“512×512″ href=“icon-512.png“ /> in index.html
  7. Zeile <script>navigator.serviceWorker.register(’service-worker.js‘);</script> in index.html

Alternativ kann man diese Voraussetzungen auch nachträglich in einem bestehenden Projekt leicht erfüllen.

Fazit

Nach all den genannten Gemeinsamkeiten und Unterschieden kann man festhalten:

  • Programmcode lässt sich (mit ein paar Änderungen) leicht zwischen Blazor-Server- und Blazor-WebAssembly-Projekten austauschen.
  • Programmcode lässt sich (mit ein paar Fallunterscheidungen) zur gemeinsamen Verwendung in Blazor Server und Blazor WebAssembly leicht in einer Razor Class Library kapseln.

Es stellt sich für die Praxis die Frage, ob man Blazor WebAssembly und/oder Blazor Server einsetzen sollte. Grundsätzlich ist Blazor ein Angebot für .NET-Entwickler, die ihr Know-how und/oder eine bestehende .NET-Programmcodebasis ins Web bringen wollen.

Blazor WebAssembly hat aufgrund der vielen zu ladenden Dateien und deren Größe sowie der fehlenden Lazy-Loading-Funktion den Nachteil, dass Nutzer (insbesondere bei schlechten Netzwerkverbindungen) beim ersten Aufruf einer Website ohne Inhalte im Cache sehr lange warten müssen, bis sie die Anwendung überhaupt nutzen können. Für öffentliche Internetangebote ist die Ladezeit deutlich zu lang, denn die Benutzer haben in der Zeit längst eine alternative Seite aufgerufen. Im Intra- oder Extranet könnte man die Anwender entsprechend instruieren, die Wartezeit zu ertragen.

Blazor-Server-Anwendungen starten hingegen sehr schnell, können aber bei dauerhaft instabilen Netzwerkverbindungen die Nerven der Benutzer belasten.

Die Entscheidung zwischen Blazor Server und Blazor WebAssembly ist folglich nicht leicht und abhängig vom Projekt, der Anzahl der Nutzer und deren Verhalten. Ich tendiere derzeit dazu, unsere bestehenden Projekte, die Blazor Server nutzen, vorerst dort zu belassen und auf Optimierungen in Blazor WebAssembly in der Zukunft zu hoffen.

Die Hoffnung auf die Zukunft hängt daran, ob Microsoft das AOT-Thema für Blazor WebAssembly möglichst schnell in den Griff bekommt und sich damit die Startzeiten verringern und die Ausführungsgeschwindigkeit erhöhen werden. Eine weitere Hoffnung ist, dass das Thema Internet Explorer sich irgendwann ganz erledigt haben wird und alle Internetnutzer wirklich WebAssembly-fähige Browser einsetzen.

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud, Azure, Serverless
Cloud-basierte & Native Apps