Teil 1: Blazor Server ist mit ASP.NET Core 3.0 erschienen

Blazor Server: Der erste Blazor-Streich
Keine Kommentare

Viele .NET-Entwickler warten seit der Erstankündigung im März 2018 sehnsüchtig auf Blazor, das .NET-basierte Web-Framework zur Entwicklung von Single Page Applications. Mit Server-side Blazor (alias Blazor Server) ist nun in .NET Core 3.0 bereits eine Form von Blazor erschienen, die sich aber nur für kleine bis mittlere Nutzerzahlen eignet.

Bei der Erstankündigung im März 2018 hatte Microsoft zunächst nur Blazor auf Basis von WebAssembly im Webbrowser vorgestellt. Hier laufen C#-Code und .NET-Assemblies direkt im Browser – wie einst bei Silverlight. Am 10. August 2018 in Preview 0.5 folgte dann Server-side-Blazor, bei dem der gleiche Programmcode auch auf dem Webserver laufen konnte. Dieses Feature wurde unter dem Stichwort „Out of Process Execution“ entwickelt. Danach bezeichnete Microsoft zur Abgrenzung das ursprüngliche WebAssembly-basierte Architekturmodell als Client-side Blazor.

Artikelserie

  • Teil 1: Blazor Server ist mit ASP.NET Core 3.0 erschienen
  • Teil 2: Blazor-Syntax, Libraries, Zustände und Interoperabilität

In der Folgezeit hat Microsoft nicht nur mit der Technik, sondern auch dem Namen der beiden Blazor-Varianten experimentiert. Zunächst sollte die serverseitige Ausführung mit „ASP.NET Core Razor Components“ einen komplett anderen Namen erhalten. Im April 2019 revidierte Microsoft das aber, was auch weise war, denn Razor Components war den vorhandenen Konzepten wie Razor Views (in ASP.NET Core MVC) und Razor Pages (eine Alternative zu ASP.NET Core MVC) und Razor Class Libraries (zur Kapselung von Razor Views und Razor Pages) namentlich zu ähnlich. Der letzte Namensstand aus dem Juli 2019, der auch für die am 23. September 2019 erschienene RTM-Version der Serverausführung gilt, ist: Die Serverausführung heißt Blazor Server, die Clientausführung Blazor WebAssembly.

Was ist Blazor Server?

Blazor Server ist stark erklärungsbedürftig, denn obwohl es auf dem Webserver läuft (wie der Namen besagt), macht es dennoch keine vollständigen Seitenrundgänge wie die klassischen Server-side Rendering (SSR) Frameworks alias Multi Page Application (MPA) Frameworks, wie z. B. ASP.NET MVC, ASP.NET Razor Pages, JSP oder PHP. Mit Blazor Server entsteht vielmehr eine Single Page Application (SPA): Der Benutzer sieht im Browser nur einen URL und es flackert auch zwischendurch nicht durch den Wechsel ganzer HTML-Seiten, denn es ändern sich bei SPA-Interaktionen nur immer einzelne Seitenteile.

Wer nun an das Feature „Server-side-(Pre-)Rendering“ denkt, das in einigen JavaScript-basierten SPA-Web-Frameworks gibt (vgl. Angular Universal, React, Vue.js, Next.js und Aurelia), erfasst damit die Fähigkeiten von Blazor Server nicht ganz. Blazor Server dient nicht nur dem Vorrendern einiger statischer Seiten auf dem Webserver, um dem Benutzer das erste Seitenerlebnis schneller zu präsentieren und währenddessen ein SPA Framework in den Browser zu laden, sondern mit Blazor Server kann ein Entwickler tatsächlich eine komplette SPA-Webanwendung erstellen und hat bei Bedarf (mit etwas eingestreutem JavaScript/TypeScript) auch vollen Zugriff auf alle Browser-APIs.

BASTA! 2020

Entity Framework Core 5.0: Das ist neu

mit Dr. Holger Schwichtenberg (www.IT-Visions.de/5Minds IT-Solutions)

Memory Ownership in C# und Rust

mit Rainer Stropek (timecockpit.com)

Softwarearchitektur nach COVID-19

mit Oliver Sturm (DevExpress)

Es stellt sich die Frage, wie das möglich ist. Abbildung 1 veranschaulicht die Architektur und Funktionsweise von Blazor Server. Auf der Serverseite läuft bei Blazor Server eine Razor Component, die neben dem obligatorischen Razor-Template-Teil (HTML-Tags mit eingestreutem C#) auch noch eine Code-behind-Datei (in C#) umfassen kann. Mit dem Razor-Template erzeugt der Softwareentwickler zwar eine komplette Webseite, diese wird aber nur beim ersten Aufruf einmalig als Ganzes zum Browser gesendet. Zusätzlich wird beim ersten Laden eine JavaScript-Bibliothek von Microsoft (blazor.server.js) zum Browser geschickt.

Abb. 1: So funktioniert Blazor Server

Abb. 1: So funktioniert Blazor Server

Blazor merkt sich auf dem Webserver das initial zum Browser gesendete HTML in Form eines virtuellen Abbilds des Browserinhalts, also des Document Object Models (DOM). Dieses Virtual DOM wird im Folgenden dafür verwendet, jegliche DOM-Änderungen festzustellen und nur noch diese zum Webbrowser zu übertragen. Die Bibliothek blazor.server.js ist clientseitig dafür zuständig, diese Änderungsmitteilungen in das echte DOM des Webbrowsers einzubauen.

Ebenso ist es Aufgabe von blazor.server.js, die Interaktionen des Benutzers mit dem Browser (Tastatureingaben und Mausklicks) zum Webserver zu übertragen und dort für Ereignisse in der Razor Component zu sorgen. Nach Abarbeitung des Ereignisses erfolgt eine Übertragung der resultierenden DOM-Änderungen zum Client. Die Übertragungen zwischen Webbrowser und Webserver erfolgt in einem von Microsoft definierten Format via ASP.NET Core SignalR über eine WebSocket-Verbindung. Microsoft nennt das neue Protokoll BlazorPack. Damit der Benutzer ein gutes Erlebnis mit der Anwendung hat, muss laut Microsoft die Netzwerklatenz unter 250 Millisekunden liegen.

Eine Blazor-Server-Anwendung ist folglich nicht offlinefähig und aufgrund des serverseitigen Virtual DOM, dass es für jeden angeschlossenen Browser gibt, auch nicht gut skalierbar. 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 Anwendung sei der RAM-Bedarf jeweils zu messen. Dass Microsoft das nicht genauer 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.

Der RAM-Bedarf ist aber kein grundsätzliches K.-o.-Kriterium für Blazor Server. Es gibt genug Webwendungen auf dieser Welt, die eine begrenzte Benutzeranzahl haben und für die das SPA-light-Konzept der Blazor-Server-Apps geeignet ist. Zudem hat man als Softwareentwickler bei Blazor Server durch die Programmiertechnik erheblichen Einfluss auf die Höhe des RAM-Bedarfs.

Blazor im Vergleich zu seinen Kollegen

Abbildung 2 zeigt den Vergleich aller fünf nun in ASP.NET Core verfügbaren Architekturalternativen:

    1. Ganz oben sieht man die Model-View-Controller-Architektur (MVC), die es seit Version 1.0 (Jahr 2016) in ASP.NET Core gibt und die es zuvor schon im klassischen ASP.NET gab. Der Controller auf dem Server füttert die View mit Daten. Die View erzeugt mit der Razor-Template-Syntax eine HTML-Seite, die als Ganzes zum Webbrowser übertragen wird. Der Browser schickt Daten per URL oder HTTP-Request-Body zurück an den Server, der dann eine neue, ganze Seite rendert und diese wieder zum Client sendet. Der Client tauscht die Seite aus, der Benutzer nimmt ein Flackern war. JavaScript zur Ausführung im Browser ist optional einstreubar in vom Webserver gerenderten HTML-Dokumente.
    2. Seit 2017 (ASP.NET Core 2.0) gibt es die Razor Pages, die ein Page Model statt eines Controllers besitzen, aber die gleiche Interaktion mit dem Webbrowser haben. Lediglich die Architektur auf dem Webserver, insbesondere die Zusammenarbeit zwischen Page Model und View ist anders.
    3. Ebenfalls im klassischen ASP.NET und in ASP.NET Core seit 1.0 gibt es das WebAPI-Modell, bei dem der Webserver lediglich Daten per REST-Dienst im JSON-Format an den Webbrowser liefert und das HTML-Rendering und die Benutzerinteraktionsereignisbehandlung komplett im Client per JavaScript/TypeScript stattfindet, in der Regel unterstützt durch SPA Webframeworks wie Angular, React, Vue.js oder Aurelia. Dieses Modell ist heute die vorherrschende Architektur bei modernen Webanwendungen. Das Modell wird aber von vielen .NET-Entwicklern nicht geliebt, weil die Clientprogrammierung mit einer anderen Programmiersprache und andersartigen Frameworks erfolgen muss, die nicht die bei .NET geschätzten Eigenschaften hinsichtlich Komfort, Produktivität und Stabilität bieten.
    4. An vierter Stelle sieht man nun den neuen Blazor Server mit dem bereits oben beschriebenen Zyklus der Übertragung von Interaktionen und DOM-Änderungen via ASP.NET SignalR.
    5. Das fünfte Architekturmodell ist Blazor WebAssembly. Die Serverseite bietet hier wieder nur noch Web-APIs an. C# läuft nun auf Basis der Mono Runtime in WebAssembly auf Browserseite.
Abb. 2: ASP.NET Core bietet mittlerweile fünf Architekturalternativen

Abb. 2: ASP.NET Core bietet mittlerweile fünf Architekturalternativen

Blazor Server vs. Blazor WebAssembly

Abbildung 3 zeigt noch einmal einen detaillierteren Architekturvergleich von Blazor Server mit Blazor WebAssembly. Bei Blazor Server läuft der C#-Programmcode auf dem Webserver auf Basis der .NET-Core-Laufzeitumgebung. Hier kann der Softwareentwickler ohne Einschränkung einer Sandbox alle .NET-Core-3.x-fähigen Assemblies (also alle Assemblies, die .NET-Standard bis Version 2.1 unterstützen) laden und nutzen.

Abb. 3: Vergleich Blazor Server mit Blazor WebAssembly

Abb. 3: Vergleich Blazor Server mit Blazor WebAssembly

Blazor WebAssembly läuft derzeit noch auf Mono (z. Z. Version 6.7) statt auf .NET Core. Das liegt einfach daran, dass das Mono-Entwicklungsteam die Idee hatte, wie man .NET mit WebAssembly kompatibel macht. In Zukunft soll bei Blazor WebAssembly auch .NET Core bzw. dessen Nachfolger .NET 5.0 [1] zum Einsatz kommen. Blazor WebAssembly läuft in der Sandbox des Browsers. Auch hier gibt es ein Virtual DOM, weil man in der WebAssembly Virtual Maschine keinen Zugriff auf das DOM des Browsers hat (Kasten: „WebAssemby“). Die JavaScript-Bibliothek blazor.webassembly.js vermittelt zwischen dem echten DOM und dem virtuellen DOM.

WebAssembly

WebAssembly (WASM) ist ein Bytecode und eine zugehörige Laufzeitumgebung zur Ausführung in Webbrowsern als Alternative zu JavaScript. WASM wird beim World Wide Web Consortium (W3C) standardisiert und hat seit dem 1. Oktober 2019 den Status Proposed Recommendation.

WASM ist nicht als Ersatz, sondern als Ergänzung zu JavaScript entwickelt worden, um komplexere Berechnungen performanter mit einer Low-Level-Sprache ausführen zu können. In WASM ist zwar Interoperabilität mit JavaScript vorgesehen, d. h. JavaScript kann WASM-Code aufrufen und umgekehrt, aber WASM hat im Standard keinen Zugriff auf das DOM des Browsers. WASM unterliegt wie JavaScript der Sandbox des Webbrowsers.

Die Bytecode Alliance mit Mozilla, Fastly, Intel und RedHat ist angetreten, WebAssembly auch außerhalb des Webbrowsers auf allen Plattformen und Geräten anzubieten. WebAssembly-basierte Anwendungen könnten also eines Tages direkt auf kleinen und großen Geräten laufen.

Die .NET-DLLs werden in ihrer üblichen Form in Microsoft Intermediate Language (MSIL) in den Webbrowser geladen. Der Just-in-time-Compiler von Mono übersetzt MSIL in WASM. Das bezeichnet man als den Interpreted Mode. Für die Zukunft ist auch eine Ahead-of-time-Kompilierung geplant (AOT Compiled Mode). Die zugehörige Aufgabe auf GitHub ist aber seit längerem offen.

Im Gegensatz zu Blazor Server ist Blazor WebAssembly noch nicht produktionsreif, sondern verweilt im Preview-Status. Immerhin ist Blazor WebAssembly seit dem 28.4.2019 ein offizielles Microsoft-Projekt; zuvor war der Status lediglich „experimentelles Projekt“. Eine erste stabile Version von Blazor WebAssembly soll im Mai 2020 (mutmaßlich im Rahmen der Build-Konferenz) erscheinen.

Browserunterstützung

Viele moderne Webbrowser haben WASM schon seit längerem implementiert: Firefox (seit Version 58), Chrome (seit Version 63), Edge (seit Version 16), Opera (seit Version 57) und Safari (seit Version 11.2). Nicht verfügbar ist WASM für den Internet Explorer. Ursprünglich war geplant, im Internet Explorer über asm.js WebAssembly zu emulieren. Dieses Features wurde jedoch wieder entfernt.

Auch Blazor Server läuft im Standard nicht im Internet Explorer. Hier kann der Softwareentwickler jedoch mit Hilfe eines Polyfills mit Namen Blazor.Polyfill die notwendigen Programmierschnittstellen nachrüsten. Der Polyfill besteht aus einer Datei blazor.polyfill.min.js, die man im Verzeichnis /wwwroot des Blazor-Server-Projekts ablegt und dann in der Host.cshtml-Datei vor dem <script>-Tag für die blazor.server.js einfügt:

<script src="~/blazor.polyfill.min.js"></script>
<script src="_framework/blazor.server.js"></script>

Projektvorlagen

In Visual Studio 2019 ab Version 16.3 sieht man im Projekt anlegen-Dialog auf oberster Ebene einen Eintrag Blazor App, der suggeriert, dass man hier sowohl eine Blazor-Server- als auch BlazorWebAssembly-Anwendung anlegen kann (Abb. 4). Im dann folgenden Assistenten sieht man auf einer der Folgeseiten aber nur noch Blazor Server App. Den in Abbildung 5 dargestellten Eintrag Blazor WebAssembly App erhält man nur, wenn man diese Vorlage zuvor separat installiert hat. Die Installation erfolgt mit der .NET Core CLI:

dotnet new --install Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview3.19555.2

Die jeweils aktuelle Versionsnummer findet man auf NuGet. Dort sind auch einige .NET-Core-Projektvorlagen als NuGet-Pakete gelistet. Während die Blazor-WebAssembly-Versionen bis März 2019 mit 0.x gezählt wurden, erhalten sie seit April 2019 die Preview-Nummerierungen von .NET Core. Für die aktuellste Blazor-WebAssembly-Version sollte man .NET Core 3.1 und Visual Studio 2019 16.4 verwenden.

Abb. 4: Blazor im Projektdialog in Visual Studio 2019 ab Version 16.3

Abb. 4: Blazor im Projektdialog in Visual Studio 2019 ab Version 16.3

Abb. 5: Blazor-Assistent in Visual Studio 2019 Version ab Version 16.3 (hier Version 16.4)

Abb. 5: Blazor-Assistent in Visual Studio 2019 Version ab Version 16.3 (hier Version 16.4)

Im Fall von Blazor Server entsteht eine C#-Projektdatei auf Basis des SDK Microsoft.NET.Sdk.Web. Sie umfasst bereits das notwendige NuGet-Paket Microsoft.AspNetCore.Blazor.Server. Explizite Referenzen auf NuGet-Pakete gibt es nur für drei Visual Studio-Analyzer-Pakete (siehe Dependencies in Abbildung 6). Auf den ersten Blick sieht man ein normales ASP.NET-Core-Projekt mit einem /Pages– und einem /Shared-Ordner wie in ASP.NET Core Razor Pages. In der Startup-Klasse ist wie üblich eine der Projektart entsprechende Middleware-Komponente eingefügt: services.AddServerSideBlazor()

Abb. 6: Aufbau eines Blazor-Server-Projekts in ASP.NET Core 3.1

Abb. 6: Aufbau eines Blazor-Server-Projekts in ASP.NET Core 3.1

Auf den zweiten Blick erkennt man, dass die meisten Razor-Dateien in /Pages und /Shared nicht die Dateinamenserweiterung .cshtml, sondern .razor tragen. Es gibt eine Razor Page (.cshtml) und acht Razor Components (.razor). Die Datei /Pages/_Host.cshtml enthält die Grundstruktur der Webseite mit <html>-, <head>– und <body>-Tags einschließlich der <script>-Referenz auf die notwendige blazor.server.js. Diese Datei enthält auch das <app>-Tag. In ASP.NET Core 3.0 sieht es noch so aus:

<app>
  @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</app>

Seit ASP.NET Core 3.1 gibt es dafür einen eleganteren Tag-Helper:

<app>
  <component type="typeof(App)" render-mode="ServerPrerendered" />
</app>

Der <component>-Tag-Helper verweist mit type=“typeof(App) auf die Datei /App.razor, die definiert, dass die zu einem URL gefundene Razor Component geladen bzw. ein Fehlertext angezeigt wird, wenn es keine Razor Component gibt. In beiden Fällen ist /Shared/MainLayout.razor als Layout-Master-Seite festgelegt (Listing 1).

<Router AppAssembly="@typeof(Program).Assembly">
  <Found Context="routeData">
    <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
  </Found>
  <NotFound>
    <LayoutView Layout="@typeof(MainLayout)">
      <p>Sorry, there's nothing at this address.</p>
    </LayoutView>
  </NotFound>
</Router>

Die via Direktive von LayoutComponentBase erbende Layout-Master-Seite wiederum lädt dann die Navigationsleiste (Komponente /Shared/NavMenu.razor) und enthält dann den Befehl @Body an der Stelle, an die die Inhalte der Razor Components bzw. der obige Fehlertext hineingerendert werden sollen (Listing 2). Abbildung 7 stellt den beschriebenen Aufbau noch einmal als Schaubild übersichtlich dar.

@inherits LayoutComponentBase
<div class="sidebar">
  <NavMenu />
</div>

<div class="main"> …
  <div class="content px-4">
    @Body
  </div>
</div>
Abb. 7: Verschachtelung der Razor Components in der Blazor-Server-Projektvorlage

Abb. 7: Verschachtelung der Razor Components in der Blazor-Server-Projektvorlage

Komponenten und Routing

Eine Razor Component besteht mindestens aus einer .razor-Datei, optional kann es auch eine Code-behind-Datei geben. Aus der Razor Component entsteht bei der Kompilierung eine .NET-Klasse. Der Name der Razor-Component-Klasse ergibt sich aus dem Dateinamen ohne .razor.Count ist also der Klassename der Razor Component Count.razor. Die Namen der Komponenten (also die Dateinamen) müssen mit einem Großbuchstaben beginnen, sonst kommt es zu folgendem Fehler: „Component ‚xy‘ starts with a lowercase character. Component names cannot start with a lowercase character.“

Jede einzelne Razor Component enthält zu Beginn eine Seitendirektive mit der Route (relativer URL), auf die die Komponente reagieren soll (Listings 3 und 4). Dabei darf eine Razor Component durchaus auf mehrere Routen reagieren; z. B. ist erlaubt:

@page "/counter"
@page "/Zähler"
@page "/vorführung/zähler"

Hier sind bewusst Umlaute und Groß- und Kleinschreibung gemischt, um zu zeigen, dass Umlaute möglich sind und die Groß- und Kleinschreibung bei den Routen-URLs irrelevant ist. Eine Razor Component Counter in Counter.razor kann also als Route /counter besitzen. Gleichwohl dürfen sich mehrere Blazor-Komponenten nicht den gleichen relativen URL verwenden. Sonst führt das zum Fehler „InvalidOperationException: The following routes are ambiguous: …“

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

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

@code {
  int currentCount = 0;

  void IncrementCount()
  {
    currentCount++;
  }
}
@page "/fetchdata"
@using BlazorS.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
  <p><em>Loading...</em></p>
}
else
{
  <table class="table">
...
    <tbody>
      @foreach (var forecast in forecasts)
      {
        <tr>
          <td>@forecast.Date.ToShortDateString()</td>
          <td>@forecast.TemperatureC</td>
          <td>@forecast.TemperatureF</td>
          <td>@forecast.Summary</td>
        </tr>
      }
    </tbody>
  </table>
}

@code {
  private WeatherForecast[] forecasts;

  protected override async Task OnInitializedAsync()
  {
    forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
  }
}

Code-behind-Code

In der Projektvorlage versteift sich Microsoft ganz auf Inline-Code (siehe @code), was für einige Nutzer abschreckend ist. Auch beim Anlegen eines neues Razor-Component-Elements in Visual Studio (Add | Add Item | Razor Component) in einem bestehenden Projekt lebt Microsoft diese Praxis leider vor. Tatsächlich unterstützt Blazor in seinen beiden Formen auch Code-behind-Dateien.

Derzeit muss man als Entwickler also noch selbst die Code-behind-Datei anlegen. Diese trägt den gleichen Dateinamen mit der Endung .razor.cs und realisiert eine Klasse, die von ComponentBase erbt. Die Klasse, die beim Kompilieren aus der eigentlichen .razor-Datei entsteht, muss mit @inherits von der Code-behind-Klasse erben. Listings 5 und 6 zeigen ein Beispiel für eine Razor Component mit Code-behind-Datei.

@page "/Vorlage"
@inherits VorlageModel;
@inject BlazorUtil Util
@inject IJSRuntime JSRuntime
@inject NavigationManager  NavigationManager
@using ITVisions.Blazor

<h2>Code-Behind-Vorlage</h2>
X: <input type="number" @bind="X" />
Y: <input type="number" @bind="Y" />
<button @onclick="Add">Add x + y</button>
Sum: @Sum

@code
{
  // Hier wäre auch noch Code möglich, wenn man unbedingt will 😉
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using ITVisions.Blazor;
using System.Threading.Tasks;
using BO; // eigene Business Objects
using BL; // eigene Business Logic


namespaceWeb
{
  public class VorlageModel : ComponentBase, IDisposable
  {
    [Inject]
    public BlazorUtil Util { get; set; } = null;
    [Inject]
    public IJSRuntime JSRuntime { get; set; } = null;

    [Parameter]
    public decimal X { get; set; } = 1.23m;
    [Parameter]
    public decimal Y { get; set; } = 2.34m; 
    
    public decimal Sum = 0;

    #region Standard-Lebenszyklus-Ereignisse
    protected override void OnInitialized()
    {
      Util.Log(nameof(VorlageModel) + ".OnInitialized()");
    }

    protected async override Task OnInitializedAsync()
    {
      Util.Log(nameof(VorlageModel) + ".OnInitializedAsync()");
    }

    protected override void OnParametersSet()
    {
      Util.Log(nameof(VorlageModel) + ".OnParametersSet()");
    }

    protected async override Task OnParametersSetAsync()
    {
      Util.Log(nameof(VorlageModel) + ".OnParametersSetAsync()");
    }

    protected override void OnAfterRender(bool firstRender)
    {
      Util.Log(nameof(VorlageModel) + ".OnAfterRender(firstRender=" + firstRender + ")");
      // this.StateHasChanged(); // --> Endlosschleife !!! 🙁
    }

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
      Util.Log(nameof(VorlageModel) + ".OnAfterRenderAsync(firstRender=" + firstRender + ")");
    }

    public void Dispose()
    {
      Util.Log(nameof(VorlageModel) + ".Dispose()");
    }
    #endregion

    #region Reaktionen auf Benutzerinteraktionen

    public void Add()
    {
      Sum = X + Y;
      Util.Log($"{nameof(VorlageModel)}.Add(). x={X} y={Y} sum={Sum}");
      ...
    }
    #endregion
  }
}

Darin sieht man auch die Realisierung der Standardereignisse OnInitialized() und OnAfterRender() sowie OnParametersSet(). Diese gibt es jeweils auch noch in einer asynchronen Variante: OnInitializedAsync(), OnAfterRenderAsync() und OnParametersSetAsync(). OnInitialized() und OnInitializedAsync() werden aufgerufen, wenn eine Komponente sichtbar wird. Die Ereignisse sind also nicht ganz vergleichbar mit dem Page.Load()-Ereignis in Web Forms bzw. dem OnGet() und OnPost() in Razor Pages, denn diese werden bei jedem eingehenden HTTP-Request aktiv.

OnAfterRender(bool firstRender) und OnAfterRenderAsync(bool firstRender) in Blazor werden nach dem Rendering gefeuert. Mit dem Parameter firstRender stellt man fest, ob das Rendering zum ersten Mal stattfindet. In diesen Ereignissen vorgenommene Änderungen an Werten werden in der Benutzeroberfläche nicht mehr sichtbar. Theoretisch kann man mit this.StateHasChanged() hier ein erneutes Rendern erzwingen. Dann wird aber das Rendering neu angestoßen und das Ereignis nochmals aufgerufen, was zu einer Renderingendlosschleife führt, wenn man es nicht entsprechend abfängt (vgl. den auskommentierten Code in Listing 6). Außerdem gibt es noch OnParametersSet() und OnParametersSetAsync(), die gefeuert werden, wenn eine Komponente von ihrem Aufrufer Parameter erhält.

Es ist nicht verboten, sowohl gleichzeitig für die synchrone als auch das asynchrone Variante eines Ereignisses eine Ereignisbehandlung zu implementieren. In diesem Fall ruft Blazor immer zuerst die synchrone Variante auf. Auch zu Reaktionen auf Benutzerinteraktionen implementierte eigene Methoden können asynchron realisiert werden. Hier ruft aber Blazor jeweils immer eine Methode auf, selbst wenn es eine asynchrone und eine synchrone Variante gibt. Diese müssten sich ja im Namen unterscheiden und der Entwickler muss im Razor-Code für das Ereignis einen eindeutigen Namen angeben, siehe z. B. @onclick=“Add“ in Listing 5.

Debugging

Ein Blazor-Server-Projekt können Entwickler – wie bei MVC und Razor Pages – wahlweise im Visual-Studio-Debugger oder über View in Browser (STRG + SHIFT + W) starten. Im Debugger können wie gewohnt Haltepunkte gesetzt werden – sowohl in den Codedateien als auch der Razor-Template-Syntax. Bei Änderungen muss man den Debugger beenden und neu starten.

Wenn die Blazor-Server-Anwendung per View in Browser startet, kompiliert Visual Studio bei Änderungen im Hintergrund automatisch nach dem Speichern der Datei neu. Im Browser sieht der Browsernutzer zunächst eine ausgegraute Webseite mit „Attempting to reconnect to the server…“ und dann „Could not reconnect to the server. Reload the page to restore functionality.“ Ein Klick auf den Reload-Link oder das Auslösen der Reload-Funktion im Browser setzt die Webanwendung unter dem zuletzt angezeigten URL fort. Der Zustand der Komponente einschließlich des Virtual DOM ist freilich verloren gegangen, d. h. der Entwickler sieht im Browser wieder die Ausgangsanzeige gemäß aktuellem persistiertem Datenbestand.

Ausblick

Das war der erste Teil dieser Serie. Im zweiten Teil in der nächsten Ausgabe wird es um die Razor-Syntax, Komponentenzustände, Razor Class Libraries und die Interoperabilität mit JavaScript gehen.

Links & Literatur

  • [1] Schwichtenberg, Holger: „R.I.P. .NET ‚Core‘“; in: Windows Developer 7.2019

Windows Developer

Windows DeveloperDieser Artikel ist im Windows Developer erschienen. Windows Developer informiert umfassend und herstellerneutral über neue Trends und Möglichkeiten der Software- und Systementwicklung rund um Microsoft-Technologien.

Natürlich können Sie den Windows Developer über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. Außerdem ist der Windows Developer weiterhin als Print-Magazin im Abonnement erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -