Test Driven Development für Blazor-Webkomponenten mit bUnit

Im vorherigen Artikel wurde das Blazor-Framework vorgestellt, mit dem mit C# Web-UIs erstellt werden können. In diesem zweiten Artikel wollen wir uns eine Test-Bibliothek ansehen, die in den Top10 der NuGet-Downloads für Blazor rangiert [1].

bUnit ist eine Bibliothek, mit der Unit Tests für Blazor-Komponenten erstellt werden. Sie ist seit 2021 als stabile Version verfügbar [2] und wird in der offiziellen Blazor-Dokumentation für Komponententests empfohlen [3].

Die Dokumentation für bUnit findet sich unter https://bunit.dev/

Razor-Syntax für eine Web-UI mit C# statt JavaScript

Alle Bausteine einer Blazor-App sind Razor-Komponenten mit der Dateiendung .razor. Darin wird die Website oder ein Teil davon in der sogenannten Razor-Syntax beschrieben. Das ist ein Mix aus HTML, speziellem Razor-Markup und C#. CSS wird zum Stylen verwendet.

Die klassische Blazor-Demo-Seite mit einem Zähler sieht so aus:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Abbildung 1: Seite Counter.razor

In diesem Beispiel ist HTML und C# in einer Datei enthalten. Für komplexere Komponenten ist es hilfreich, beides voneinander zu trennen, indem eine code-behind-Datei angelegt wird. Die Datei für unsere Komponente Counter.razor heißt dann Counter.razor.cs. Der Compiler verknüpft die beiden Dateien automatisch miteinander, sodass uns IntelliSense beim Programmieren auf die Sprünge hilft.

Um die Code-Ausschnitte in diesem Artikel kürzer zu halten, wurde für alle Beispiele Markup und C# mit einer code-behind-Datei getrennt.

Komponenten in Blazor: Razor Components

Um Elemente zu kapseln und wiederverwendbar zu machen, realisiert man Funktionalitäten in Komponenten. Diese enthalten nur das Markup und den Code für eine einzelne Funktionalität. In der Website werden dann nur Komponenten eingefügt und eventuell Parameter und Callbacks angebunden. Komponenten können in eine Library verschoben und als NuGet-Pakete bereitgestellt werden.

Das Beispiel zeigt den gleichen Counter, nachdem er in einer Komponente CounterComponent.razor gekapselt wurde. Das visuelle Ergebnis im Browser ist gleich.

@page "/counter"
@using BlazorWasmTemplateApp.Components
<PageTitle>Counter</PageTitle>

<h1>CounterComponent</h1>
<CounterComponent/>

Abbildung 2: Seite Counter.razor mit Komponente

<p id="currentCount" role="status">Current count: @currentCount</p>

<button id="incrementCountButton" class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Abbildung 3: Markup für Counter-Komponente CounterComponent.razor

using Microsoft.AspNetCore.Components;

namespace BlazorWasmTemplateApp.Components
{
    public partial class CounterComponent : ComponentBase
    {
        private int currentCount = 0;

        private void IncrementCount()
        {
            currentCount++;
        }
    }
}

Abbildung 4: Code für Counter-Komponente CounterComponent.razor.cs

Unit Tests für die UI

Üblicherweise wird die UI einer Anwendung mit Ende-zu-Ende-Tests (E2E) überprüft. In der Web-Entwicklung kommen dafür Frameworks wie Selenium oder Playwright zum Einsatz. Diese Tests starten die gesamte Anwendung, rendern sie in einem Browser und führen dann die Testschritte durch. In solchen Tests wird eine Funktionalität oft durch die gesamte Anwendung hindurch getestet. Aufgrund der Komplexität sind E2E-Tests oft wartungsintensiv und langsam. Daher ist die allgemein anerkannte Empfehlung, nur einige wenige E2E-Tests und viele Komponententests zu schreiben (Testpyramide, siehe [4]).

Wenn man die UI testgetrieben nach den Prinzipien von Test Driven Development (TDD) entwickelt oder eine Benutzeroberfläche mit umfangreicher Logik programmiert, benötigt man viele Tests, die in kurzer Zeit ausgeführt werden. Daher hat es in solchen Fällen Nachteile, wenn man ausschließlich auf E2E-Tests setzt. bUnit-Tests sind das Gegenteil zu E2E-Tests, sie sind übersichtlich, isoliert voneinander und vor allem schnell. Ein Test benötigt oft nur einige Millisekunden für die Ausführung und fühlt sich an wie ein Unit Test im Backend.

bUnit ist auch eine gute Wahl, wenn man eine Bibliothek von Razor-Komponenten aufbaut. Nicht umsonst finden sich unter den Hauptsponsoren von bUnit Firmen wie Progress Telerik und Syncfusion, die u.a. Bibliotheken von Blazor-UI-Komponenten vertreiben.

Unit Tests für die UI mit bUnit sind also eine gute Ergänzung zu E2E-Tests, um Details der UI zu testen.

Erste Schritte mit bUnit

Vorbereitungen

bUnit ist kein Framework, sondern auf die Test Runner xUnit, NUnit oder MSTest angewiesen. Für die Beispiele in diesem Artikel wird xUnit verwendet. Die Ausführung der Tests erfolgt wie gewohnt über die IDE oder über die Kommandozeile mit dotnet test.
Da wir mit bUnit Razor-Komponenten testen wollen, müssen wir das SDK in der Projektdatei unseres Testprojekts auf Microsoft.NET.Sdk.Razor ändern. Details dazu finden sich in der bUnit-Dokumentation unter [5].

bUnit ist mit Test-Projekten in der Version .NET Standard 2.1 und mit allen .NET Core-Versionen ab 3.1 kompatibel.

Tests können in reinem C# oder in Razor-Syntax programmiert werden. Aus Gründen der Verständlichkeit wird ausschließlich die Variante mit C#-Code gezeigt. Mit Razor-Syntax ist es etwas einfacher, Parameter und Callbacks zu übergeben und HTML direkt mit einem erwarteten Markup zu vergleichen. Da ein Vergleich von HTML aber zu schlecht wartbaren Tests führt, ist dieser Vorteil der Razor-Syntax in der Praxis unwichtig.

Der erste Test

Wie könnte ein Test unserer CounterComponent aussehen? Wir wollen überprüfen, ob unser Zähler im Ausgangszustand 0 und nach einem Klick 1 anzeigt.

using Bunit;

namespace Tests
{
    public class CounterComponentTests : TestContext
    {

        [Fact]
        public void CurrentCount_IsZero_ByDefault()
        {
            //Arrange
            var componentUnderTest = RenderComponent<CounterComponent>();

            //Act
            AngleSharp.Dom.IElement paragraph = componentUnderTest.Find("#currentCount");

            //Assert
            Assert.Equal("Current count: 0", paragraph.InnerHtml);
        }

        [Fact]
        public void CurrentCount_Increases_WhenButtonClicked()
        {
            //Arrange
            var componentUnderTest = RenderComponent<CounterComponent>();
            AngleSharp.Dom.IElement button = componentUnderTest.Find("#incrementCountButton");

            //Act
            button.Click();

            //Assert
            AngleSharp.Dom.IElement paragraph = componentUnderTest.Find("#currentCount");
            Assert.Equal("Current count: 1", paragraph.InnerHtml);
        }
    }
}

Abbildung 5: Einfacher Test der CounterComponent

Die generische Methode RenderComponent<TComponent>() mit TComponent als Typ der zu testenden Komponente erstellt ein Rendering. Dieses Rendering unserer CounterComponent mit seinen Attributen, Methoden und Eigenschaften entspricht der Darstellung der Komponente in einem Browser.

Um Interaktionen durchzuführen und den Inhalt des Renderings zu überprüfen, können wir Elemente mit der Methode Find() identifizieren. Diese Methode erwartet einen CSS-Selektor vom Typ string als Parameter. Ein CSS-Selektor ist ein Filter für die Elemente in einem HTML-DOM, der nach CSS-Klassen, Attributen, IDs oder HTML-Element-Typen filtern kann. Eine gute Referenz für CSS-Selektoren findet sich unter [6].

Der CSS-Selektor ‚#‘ filtert nach der ID des Elements. Der Selektor #currentCount filtert also das Element mit id=“currentCount“ heraus, das in unserer Beispielkomponente den aktuellen Wert des Zählers enthält.

Um zu ermitteln, ob unser Test erfolgreich ist, vergleichen wir den HTML-Inhalt mit einem erwarteten Text. Solche Tests sind fragil und wartungsintensiv, aber ein gutes Demo-Beispiel.

Komponenten mit Parametern testen

Wie sieht ein Test für eine Komponente mit Parametern aus? Dazu erweitern wir unsere CounterComponent um einen Parameter, mit dem wir den Startwert setzen können. Da wir unsere Komponente testgetrieben entwickeln wollen, fügen wir als erstes einen neuen Test hinzu. Darin nutzen wir eine Überladung der Methode RenderComponent() mit der man per Lambda-Ausdruck Parameter an einen ParameterBuilder übergibt.

Da wir testgetrieben vorgehen und es den Parameter StartValue in unserer CounterComponent noch gar nicht gibt, ist unser Test jetzt noch nicht kompilierbar.

[Fact]
public void CurrentCount_IsNotZero_WhenStartValueSet()
{
    //Arrange
    int startValue = 42;
    var componentUnderTest = RenderComponent<CounterComponent>(
        parameterBuilder => parameterBuilder.Add(param => param.StartValue, startValue));

    //Act
    AngleSharp.Dom.IElement paragraph = componentUnderTest.Find("#currentCount");

    //Assert
    Assert.Equal("Current count: " + startValue, paragraph.InnerHtml);
}

Abbildung 6: Test für unsere Komponente mit einem Parameter

Damit der Test kompiliert, müssen wir unsere Komponente CounterComponenterweitern. Im ersten Schritt fügen wir das Property StartValue vom Typ inthinzu und versehen es mit dem Attribut Parameter. Jetzt kann unsere Komponente kompiliert werden. Anschließend führen wir unseren neuen Test aus und sehen, dass er fehlschlägt. Wir befinden uns also noch in der roten Phase des Red-Green-Refactor-Zyklus von TDD und müssen die Implementierung anpassen. Dazu initialisieren wir unsere Variable currentCountin der Methode OnParametersSet(). Diese Methode wird von Blazor aufgerufen, nachdem die Parameter an eine Komponente übergeben wurden. Jetzt können wir den Test erneut ausführen und sehen, dass er erfolgreich ist. Wir sind also in der grünen Phase unseres TDD-Zyklus. Wir verzichten hier auf Änderungen in der Refactoring-Phase eines TDD-Zyklus.

Die code-behind-Datei unserer Komponente sieht nun so aus:

using Microsoft.AspNetCore.Components;

namespace BlazorWasmTemplateApp.Components
{
    public partial class CounterComponent : ComponentBase
    {
        [Parameter]
        public int StartValue { get; set; } = 0;

        private int currentCount = 0;

        protected override void OnParametersSet()
        {
            currentCount = StartValue;
        }

        private void IncrementCount()
        {
            currentCount++;
        }
    }
}

Abbildung 7: Die um einen Parameter erweiterte Komponente

Komponenten mit Events testen

Ein weiteres typisches Element von Razor-Komponenten sind Events. Nehmen wir an, dass unsere Website informiert werden möchte, wenn sich der Wert unseres Zählers ändert. Dazu erweitern wir unsere Tests abermals um den folgenden Test:

[Fact]
public void OnCountIncremented_WasInvoked_WhenButtonClicked()
{
    //Arrange
    bool isEventInvoked = false;
    var componentUnderTest = RenderComponent<CounterComponent>(
        parameterBuilder => parameterBuilder
.Add(param => param.OnCountIncremented, () => isEventInvoked = true));

    AngleSharp.Dom.IElement button = componentUnderTest.Find("#incrementCountButton");

    //Act
    button.Click();

    //Assert
    Assert.True(isEventInvoked);
}

Abbildung 8: Test für unsere Komponente mit einem Event

Auch hier ist unser Test noch nicht kompilierbar. Daher ergänzen wir unsere Komponente. Wir fügen das neue Property OnCountIncremented vom Typ EventCallback hinzu und versehen es wie gehabt mit dem Attribute Parameter. Jetzt ist unser Test kompilierbar, schlägt aber noch fehl. Damit im nächsten Schritt unser neuer Test erfolgreich ist, rufen wir den Callback in der Methode IncrementCount() unserer Component auf.

using Microsoft.AspNetCore.Components;

namespace BlazorWasmTemplateApp.Components
{
    public partial class CounterComponent : ComponentBase
    {
        [Parameter]
        public int StartValue { get; set; } = 0;

        [Parameter]
        public EventCallback OnCountIncremented { get; set; }

        private int currentCount = 0;

        protected override void OnParametersSet()
        {
            currentCount = StartValue;
        }

        private async Task IncrementCount()
        {
            currentCount++;
            await OnCountIncremented.InvokeAsync();
        }
    }
}

Abbildung 9: Die um einen Event erweiterte Komponente

Subkomponenten

Razor-Komponenten können in sich wieder Razor-Komponenten enthalten (Subkomponenten). Mit bUnit ist es ist möglich, diese Subkomponenten im DOM zu finden, mit Parametern zu versehen, erneut zu rendern und Asserts durchzuführen. Ob solche Tests eine gute Idee sind, hängt vom Szenario ab. Um Abhängigkeiten zu verringern, ist es mit bUnit möglich, Komponenten durch Test Doubles zu ersetzen. Das kann vor allem beim Einsatz von Bibliotheken eines Drittanbieters nützlich sein.

Dependency Injection

Neben Parametern und Events ist es in Razor-Komponenten üblich, Abhängigkeiten durch Dependency Injection zu injizieren. Das Blazor-Framework nutzt dafür die Dependency Injection von ASP.NET Core. bUnit macht deshalb die Services-Kollektion in der Basisklasse TestContext verfügbar, die aus ASP.NET Core bekannt ist. Dort können Test Doubles registriert werden, bevor die Komponente gerendert wird. Für Abhängigkeiten wie IJsRuntime, HttpClient, NavigationManager etc., die den Komponenten normalerweise automatisch durch ASP.NET Core bereitgestellt werden, gibt es Anleitungen für Test Doubles in der Dokumentation von bUnit.

Fazit

Mit bUnit gibt es auch für Blazor eine Möglichkeit, um Komponenten isoliert, schnell und ohne E2E-Tests unter die Lupe zu nehmen. Damit steht für Blazor-Entwickler ein wichtiges Tool zur Verfügung, um mit Tests die Wartbarkeit der Blazor-App sicherzustellen. Außerdem wird es möglich, auch die UI testgetrieben zu entwickeln. Damit steht Blazor auch in diesem Aspekt anderen SPA-Frameworks in nichts nach.

Quellen

[1] nuget.org. [Online]. Available: https://www.nuget.org/packages?q=Tags%3A%22Blazor%22&sortby=totalDownloads-desc. [Zugriff am 22 Juni 2023].

[2] E. Hansen, „bUnit Releases,“ GitHub, [Online]. Available: https://github.com/bUnit-dev/bUnit/releases?page=3. [Zugriff am 22 Juni 2023].

[3] L. Latham, R. Anderson und GitHubPang, „Test Razor components in ASP.NET Core Blazor,“ Microsoft Learn, 04 April 2023. [Online]. Available: https://learn.microsoft.com/en-us/aspnet/core/blazor/test. [Zugriff am 22 Juni 2023].

[4] M. Cohn, Succeeding with Agile: Software Development Using Scrum, Addison-Wesley, 2009.

[5] E. Hansen, „Creating a new bUnit test project,“ bUnit, 7 April 2023. [Online]. Available: https://bunit.dev/docs/getting-started/create-test-project.html. [Zugriff am 22 Juni 2023].

[6] Refsnes Data, „CSS Selector Reference,“ W3Schools, [Online]. Available: https://www.w3schools.com/cssref/css_selectors.php. [Zugriff am 22 Juni 2023].

[7] E. Hansen, „bUnit Documentation,“ 2023. [Online]. Available: https://bunit.dev/docs/getting-started/index.html. [Zugriff am 22 Juni 2023].

Web-UIs mit Blazor und mögliche Anwendungsfälle in der Industrie

Blazor ist ein Framework von Microsoft, um interaktive Web-Frontends mit C# statt mit JavaScript zu erstellen. Es wurde 2019 im Rahmen von .NET Core 3.0 veröffentlicht. Seitdem wird es konstant weiterentwickelt. So wird es beispielsweise auch mit .NET 8 weitere Verbesserungen geben [1]. Parallel zu den Features von Blazor reift auch das Ökosystem an hilfreichen Komponenten und Bibliotheken. Mittlerweile hat sich das Framework bewährt und den anfänglichen Hype hinter sich gelassen. Daher soll nachträglich genauer betrachtet und erläutert werden wo Blazor ggf. im industriellen Kontext eingesetzt werden kann.

C# statt JavaScript – Die Gestaltung der UI

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Abbildung 1: Codebeispiel mit Razor-Syntax für eine Blazor-Website

Blazor verwendet die sogenannte Razor-Syntax. Diese besteht aus HTML, speziellem Razor-Markup und C#. Mit Grundkenntnissen in HTML findet man sich darin auch als C#-Entwickler schnell zurecht. JavaScript spielt bei Blazor im Normalfall nur im Hintergrund eine Rolle und wird in den meisten Fällen nicht direkt eingesetzt. Ausnahmen von dieser Regel sind z.B. spezielle Interaktionen mit dem Browser oder um JavaScript-Bibliotheken anzusprechen. Gerade für beliebte JavaScript-Bibliotheken gibt es jedoch auch immer häufiger entsprechende Wrapper, welche durch die Community bereitgestellt werden.

Hosting-Modelle von Blazor

Für Blazor-Apps gibt es drei Hosting-Modelle, die bestimmen, wie eine Blazor-App funktioniert, welche Funktionen zur Verfügung stehen und welche Einschränkungen es gibt.

Blazor Server – Server-Side Rendering

In diesem Fall wird die Webseite in einer ASP.NET Core-App auf dem Server dynamisch erzeugt und das HTML an den Browser übertragen. Durch das Rendering auf dem Server steht die gesamte Funktionalität des Backends zur Verfügung. Für jede Interaktion zwischen Browser und Server wird eine aktive SignalR-Verbindung benötigt. Die Latenz dieser Netzwerkverbindung wirkt sich direkt auf die Reaktionszeit der UI aus. Außerdem wird die Performance der Blazor-Anwendung durch die Performance des Servers begrenzt, was zum Beispiel viele gleichzeitige Client-Verbindungen kostspielig macht.

Dieses Hosting-Modell ist gut geeignet, wenn …

  • Anwendungen eine überschaubare Anzahl von Nutzern bedienen.
  • Serverperformance, Netzwerklatenz und Offline-Szenarios keine Rolle spielen.
  • spezielle Anforderungen bestehen, die nur im Backend umgesetzt werden können. Beispiel:  Authentifizierung von Usern auf der Basis des Windows-Logins
Abbildung 2: Architektur einer Blazor Server-App

Blazor WebAssembly – Client-Side Rendering

Die Anwendung wird zusammen mit der .NET-Runtime von einem beliebigen Webserver in den Browser geladen. Diese Runtime wird in der WebAssembly-Umgebung des Browsers ausgeführt und rendert anschließend die Webseite lokal. Nach dem Download der gesamten App vom Server laufen Blazor WebAssembly-Apps somit komplett im Browser. Das initiale Laden der App dauert jedoch einige Sekunden, da hier – je nach App – viele Daten übertragen werden müssen (bereits für die Template-App von Microsoft sind es 10 MB). Blazor WebAssembly entspricht der Funktionsweise von gängigen Single Page Application-Frameworks (SPA) wie React oder Angular.

Da die App komplett im Browser läuft, ist sie nicht auf einen Server angewiesen, aber auf die Funktionalitäten des Browsers und der WebAssembly-Runtime beschränkt, was zum Beispiel die Interaktion mit lokalen Ressourcen auf dem Client (Dateisystem etc.) und einige .NET-APIs limitiert.

Dieses Hosting-Modell ist gut geeignet, wenn …

  • keine Firewall den Download der App (DLLs!) in den Browser verhindert.1
  • Download-Größe und Startzeit irrelevant sind oder vom Benutzer akzeptiert werden.
  • eine reichhaltige und interaktive Single Page Application benötigt wird.
  • alle Funktionalitäten im Browser realisierbar sind.
Abbildung 3: Architektur einer Blazor WebAssembly-App

Blazor Hybrid

Die Blazor-App wird lokal auf dem Desktop (in .NET MAUI, WPF oder Windows Forms) ausgeführt und in einem eingebetteten WebView gerendert. Die Wortwahl ‘Hybrid’ bezieht sich also auf die Verschmelzung von Webtechnologie (Blazor) mit Desktop- oder Mobile-Frameworks. Da die App lokal läuft, hat sie auch vollen Zugriff auf Funktionalitäten des Clients.

Dieses Hosting-Modell ist gut geeignet, wenn …

  • Blazor-Komponenten auf dem Desktop wiederverwendet werden sollen
  • Desktop-Anwendungen Schritt für Schritt auf Webtechnologie umgestellt werden sollen
Figure 4:Architektur einer Blazor-Hybrid-App

Das im Herbst 2023 erscheinende .NET 8 wird ein weiteres Hosting-Modell einführen, das einen Mittelweg zwischen Blazor Server und WebAssembly darstellt. Damit sollen Performance-Vorteile des Renderns von statischen Inhalten auf dem Webserver mit der Interaktivität von Single Page Applications verbunden werden und die Ladezeit der Apps verringert werden [2].

Wo kann Blazor in der Industrie zum Einsatz kommen?

Für die Bewertung von sinnvollen Anwendungsfällen müssen wir uns die Vorteile von Blazor gegenüber anderen Web-UI-Frameworks wie Angular oder React ansehen.

  • Blazor gehört zum ASP.NET Core-Universum und kann damit vorhandene .NET-Bibliotheken einbinden und weiterverwenden.
  • Da Blazor für C#-Entwickler entwickelt wurde, passt es zu Teams, deren Kompetenzen bisher vor allem im .NET-Backend-Bereich liegen.
  • Blazor passt gut zu Projekten, in denen der Fokus eher auf dem Backend mit komplexer Geschäftslogik und umfangreicher Datenbank liegt. Beispiel: Forms-over-Data-Anwendungen mit umfangreichen Formularen, aber ansonsten geringer UI-Funktionalität

Anwendungsfall 1: Hintergrund-Anwendung mit lokaler Management-Oberfläche

Eine .NET-Anwendung, die auf einem Rechner als Dienst läuft, soll eine einfache lokal verfügbare UI bekommen, mit der die Software konfiguriert und administriert werden kann.

Abbildung 5: Skizze der Anwendung in Anwendungsfall 1

Für diesen Fall empfiehlt sich Blazor mit dem Hosting-Modell Blazor Server, da in der Blazor-Anwendung einfach auf Komponenten der .NET-Hintergrundanwendung zugegriffen werden kann. Abstraktionsschichten wie eine REST-API sind nicht nötig. Netzwerkprobleme durch Firewall, hohe Latenz oder fehlende Verfügbarkeit sind ausgeschlossen. Durch die geringe Hürde beim Umstieg auf Blazor können auch Backend-Entwickler mit geringen Kenntnissen von Web-Techniken die einfache Web-UI umsetzen.

Anwendungsfall 2: Informationssystem

Ein System stellt Informationen für Anlagenwartung zur Verfügung. Die UI soll an mehreren festen Stationen und mobil auf Laptop oder Tablet zur Verfügung stehen, um auch vor Ort an der Maschine auf die Daten zugreifen zu können.

Abbildung 6: Skizze der Anwendung in Anwendungsfall 2

Hier empfiehlt sich Blazor WebAssembly. Für den mobilen Zugriff in einer Anlage muss ein Offline-Szenario unterstützt werden, bei dem die Daten auch ohne Verbindung lokal gelesen und bearbeitet werden können. Die Daten werden zu einem späteren Zeitpunkt mit dem Backend synchronisiert. Blazor WebAssembly unterstützt auch die Funktionen für eine Progressive Web App (PWA), die sich lokal installieren lässt. Die Startdauer der App ist erhöht, wird aber im Industrieumfeld in den meisten Fällen akzeptiert.

Die Netzwerkverbindung zwischen Webserver und den Endgeräten ist in diesem Szenario unbedingt zu Beginn zu klären. Gibt es Firewalls, die den Download der Anwendung verhindern?

Anwendungsfall 3: Steuerungsrechner

Eine Steuerungssoftware benötigt eine UI, um eine Maschine oder einen Prozess zu bedienen. Das Backend der Steuerungssoftware ist umfangreich, die UI hat je nach Anwendungsfall einen kleineren bis mittleren Umfang.

In diesem Szenario gibt es keine klare Empfehlung. Faktoren bei der Entscheidung sind:

  • Zugriff auf lokale Ressourcen
  • Lokaler oder verteilter Zugriff auf die UI
  • Umfang der UI

Muss die UI zwingend auf lokale Ressourcen des Steuerungsrechners zugreifen, kommt man um Blazor Server kaum herum. Damit lassen sich diese Ressourcen einfach und schnell einbinden.

Ist kein direkter Zugriff auf lokale Ressourcen aus der UI nötig, ist die Art des UI-Zugriffs eine Entscheidungshilfe. Wird auf die UI vorrangig lokal vom Steuerungsrechner aus zugegriffen, erfüllt Blazor Server meist die Anforderungen. Wird verteilt auf die UI zugegriffen, zum Beispiel von mehreren Bedienstationen eines Leitstands aus, dann ist Blazor WebAssembly die bessere Wahl. Dann haben Netzwerklatenz und Auslastung des Steuerungsrechners keinen Einfluss auf die Reaktionszeit der UI.

Fällt die Wahl auf Blazor WebAssembly, ist wie in Anwendungsfall 2 das Netzwerk zu überprüfen.

Je umfangreicher die Funktionen der UI in einem verteilten Szenario, desto mehr sollte man neben Blazor WebAssembly auch die etablierten Frameworks wie Angular, React & Co in Betracht ziehen. Für umfangreiche UIs wird schnell ein eigenes Teammitglied benötigt, das sich ausschließlich darum kümmert. In diesen Fällen hängt die Wahl der Technologie auch davon ab, welche Kompetenzen die verfügbaren Mitarbeiter besitzen.

Fazit

In den meisten Fällen erfüllt Blazor sein zentrales Produktversprechen, es ist kein Knowhow in JavaScript erforderlich. Dadurch kann die UI auch durch Teams mit .NET-Hintergrund erstellt werden, die bisher noch nicht tief in die Webentwicklung eingestiegen sind.

Welche „Geschmacksrichtung“ von Blazor, welches Hosting-Modell gewählt wird, hängt vom Anwendungsfall ab. Exemplarisch wurden drei Anwendungsfälle aus der Industrie vorgestellt.


1 Microsoft hat die Probleme der Nutzer erhört und plant in .NET 8 ein anderes Format für die Assembly-Dateien anstatt von DLL. Details und Status: https://github.com/dotnet/runtime/issues/80807

Literatur

[1] Microsoft, „.NET Blog,“ 13 Juni 2023. [Online]. Available: https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-5/. [Zugriff am 21 Juni 2023].

[2] Microsoft, „GitHub,“ 14 Februar 2023. [Online]. Available: https://github.com/dotnet/aspnetcore/issues/46636. [Zugriff am 21 Juni 2023].