Blazor lernen: Mit C# in den Browser

Vor ein paar Tagen ist die neuste dotnetpro zu uns ins Haus geflattert. Zu meiner Begeisterung thematisiert die Ausgabe 05/21 die von unserem A-Team bevorzugte Technologie zur Entwicklung modernster Webapplikationen: Blazor.

bilder/Blazor_mit_Wolfgang_Lutz_von_der_K_K_Software_AG.png Kommentar zur dotnetpro Ausgabe 05/21

Circa eine Dekade arbeite ich schon mit ASP.NET und seinem auf .NET Core basierenden Sukzessor. Diesem Framework liegt nicht nur WebForms, MVC, WebAPI, SignalR und Razor Pages, sondern seit einiger Zeit auch Blazor zugrunde. Wir beobachteten gespannt das anfänglich von Steve Sanderson gestartete Projekt seit seiner Entstehung 2017 und erkannten früh das enorme Potential, während Angular und Co bei uns noch ein großes Thema in der Webentwicklung war.

Da wir mittlerweile bereits einige Projekte unterschiedlicher Größe und Komplexität geblazored haben, versuche ich ergänzend zum Artikel des Themenschwerpunktes erwähnter Zeitschrift zusätzliche Informationen aus erster Hand zu liefern und einen nützlichen Kommentar zu hinterlassen. Hast du die Ausgabe nicht gelesen und interessierst dich trotzdem für Blazor, JavaScript und Angular, bist auch du hier genau richtig!

Die Heirat von Blazor und Angular

Der/Die interessierte Leser*in und potentielle Blazor-Einsteiger*in wird zu Beginn der Zeitschrift mit einem beeindruckend kreativen Proof-of-Concept konfrontiert. Nur leider versäumt der Autor zu erwähnen, welchen Zweck er mit diesem Experiment verfolgt und welche konkreten Vorteile er sich hierdurch erwartet. Die Gründe warum mir diese – auf sogenannten Glue-Code basierende – Architektur zum Scheitern verurteilt scheint, liegt im offensichtlichen Ziel von Blazor: Die Nutzung von C# zur Erstellung browserfähiger Anwendungen, ohne an JavaScript oder klassische Limitationen des HTTP-Protokolls gebunden zu sein, was die Komplexität und Lernkurve für C# Programmierer massiv reduziert.

Ich möchte Angular auf keinen Fall diskreditieren, aber es hat einfach ab dem Zeitpunkt keine Rolle mehr in unseren neuen Webprojekten gespielt, als Server-Side Blazor ready for production war. Zwar mag die JavaScript-Interoperabilität besonders für diejenigen Entwickler*innen interessant sein, die ihre bestehenden Frontend Bibliotheken verwenden möchten und diese in eine Blazor-Komponente wrappen möchten, jedoch trifft dieser Edge-Case auf einen kleinen Teil der .NET-Community zu. Ich möchte jedenfalls keinem Arbeitskollegen zumuten npm installieren zu müssen, von dem wir aus gutem Grund schon länger Abstand halten. Bei der Integration von Angular verzichtet man auch bewusst auf die konsistent strenge Typisierung von C#, Intellisense und diversen Debugging-Features von Visual Studio.

Gibt es Alternativen?

Im genannten Artikel wird das <angular-grid> verwendet, um Daten tabellarisch anzuzeigen. Für diesen konkreten Anwendungsfall haben wir die DataGrid-Komponente des Radzen.Blazor NuGet Pakets lieben gelernt, woran nicht nur klassische IEnumerables wie Listen, sondern auch OData Services, IQueryables oder komplett dynamische Daten gebunden werden können. Somit lässt sich der seitenlange Quellcode für View-Markup und -Logik exemplarisch vereinfachen:

@using Radzen.Blazor
@inject IStarTrekService starTrekService

<RadzenGrid TItem="StarTrekCharacter" LoadData="LoadCharacters" Data="characters">
    <Columns>
        <RadzenGridColumn TItem="StarTrekCharacter" Property="Name" />
        <RadzenGridColumn TItem="StarTrekCharacter" Property="IsAlternateReality" Title="Alternate Reality" />
        <RadzenGridColumn TItem="StarTrekCharacter" Property="YearOfDeath" Title="Year Of Death" />
    </Columns>
</RadzenGrid>

@code {
    IEnumerable<StarTrekCharacter> characters;
    async Task LoadCharacters(LoadDataArgs args)
    {
        this.characters=this.starTrekService.GetCharacters(args.Filter, args.Skip, args.Top, args.OrderBy);
    }
}
Quellcode der Characters.razor-Komponente, die auf dem Radzen.Blazor NuGet Paket basiert.

Der Vorteil dieses Programmcodes ist die außerordentliche Lesbarkeit. Steigt die Anzahl der Codezeilen mit der Zeit, sollte man unbedingt in Erwägung ziehen, die (natürlich bestenfalls ausschließlich in C# geschriebenen) Lade- und Interaktionslogiken in eine separate Datei auszulagern, um langfristig den Überblick zu behalten. Hierbei hat sich die Namenskonvention *.razor.cs etabliert. Dadurch werden die Dateien der ViewModels bzw. partial-Klassen im Solution Explorer direkt unter den Views geschachtelt, womit man den Code nicht unnötig über verschiedene Verzeichnisse oder gar Projekte verstreut. Außerdem muss ich nur F5 drücken bzw. dotnet run eingeben um das Programm zu starten, da das referenzierte NuGet Paket automatisch installiert wird.

Als Ergebnis erhalten wir diese schöne Tabelle, die von Haus aus Sortierung, Filterung, Pagination, Responsiveness und vieles mehr unterstützt:

Radzen.Blazor DataGrid
DataGrid des Radzen.Blazor NuGet Pakets

Den kompletten Funktionsumfang dieser und weiterer Blazor-Komponenten von Radzen findet man schön beschrieben in der offiziellen Dokumentation. Der/Die aufmerksam gewordene Angular-Entickler*in der es dieses Datagrid angetan hat und trotzdem kein Blazor verwenden möchte, kann einen Blick auf die Angular-Variante werfen. Ansonsten sind die Blazor Komponenten sowohl in der WebAssembly runtime, als auch im Server Side Blazor verwendbar.

Tipps und Tricks

Um dem/der neugierigen Leser*in das Thema ein bisschen schmackhafter zu machen präsentiere ich ein paar Features, die ich an Blazor besonders zu schätzen weiß. Selbstverständlich wurde darauf geachtet den nächsten Ausgaben der dotnetpro nichts vorweg zu nehmen. Wer also mehr über das Erstellen von Komponenten, Routing, Datenbindung und Ereignisbehandlung in Blazor erfahren möchte, sollte unbedingt einen Blick in die kommenden Ausgaben werfen!

1. Host.CreateDefaultBuilder()

Diesen Methodenaufruf findet man in jedem frischen Blazor Projekt, genauer gesagt in der CreateHostBuilder() Methode der Program.cs. Die Hauptaufgaben die hier noch vor dem Start des ASP.NET Hosts übernommen werden sind die Konfiguration und Bereitstellung der IConfiguration-, IHostEnvironment-Instanzen, die in der Anwendung über den ServiceProvider zur Verfügung stehen.

Nachdem ihr CreateDefaultBuilder() aufgerufen habt, stehen euch zum Beispiel die Einstellungen - die vorher in den Umgebungsvariablen, appsettings.json und von den User Secrets konfiguriert wurden - zur Verfügung. Würde sich also beispielsweise diese appsettings.json in unserem ContentRootPath befinden: { "ApiKey": "FooBarSecret" }, könnten wir im Code ganz einfach darauf zugreifen:

public string ApiKey { get; init; }
// Bereitstellen der Konfiguration via 'Constructor Injection'
public Startup(IConfiguration configuration)
{
    this.ApiKey=configuration.GetValue<string>("ApiKey", "DefaultSecret");
}

2. Blazor CSS Isolation

Um diese Funktion zu aktivieren, muss man lediglich innerhalb des <head> Elements das automatisch dynamisch generierte Stylesheet referenzieren: <link href="Projektname.styles.css" rel="stylesheet" />. Hält man nun beim Erstellen der .css-Dateien die Namenskonvention *.razor.css strikt ein und platziert sie im gleichen Verzeichnis wie die .razor-Datei der Komponente, werden - statt wie in den meisten anderen Webframeworks - nur die relevanten Styles geladen. Dadurch wird nicht nur der CSS-Speicherbedarf signitifkant minimiert, sondern auch eine optimale Lösung des uralten Problems des unwartbaren CSS zur Verfügung gestellt. Lange CSS Selektoren werden automatisch vermieden, ohne dass man vor Konflikte fürchten muss.

3. Peformance Verbessern

Früher oder später wird jede/r Entwickler*in damit konfrontiert: Die Anwendung läuft einfach nicht so performant wie man möchte. Jedes Framework hat ganz individuelle Aspekte, die man für eine reibungslose Benutzererfahrung berücksichtigen muss. Wie immer ist es ratsam diese im Vorfeld zu kennen, um essentielle Architektur-Entscheidungen kompetent treffen zu können. Glücklicherweise hat Microsoft diesen ausführlichen Guide bereitgestellt, der auch einige technischen Hintergründe prägnant erläutert. Hier findet man neben der ein oder anderen Möglichkeit bereits leistungsschwache WASM-Szenarien, unter anderem mit ShouldRender oder dem Auslagern wiederverwendbarer RenderFragments, mit "wenig" Aufwand zu optimieren, auch Tipps zur optimalen Verschachtelung von Komponenten oder zur Virtualisierung, welche sinnvollerweise meist besser im Vorfeld bedacht werden sollten.

@{
    RenderFragment list = @<Liste>
        <Eintrag Key="1">eins</Eintrag>
        <Eintrag Key="2">zwei</Eintrag>
        <Eintrag Key="3">drei</Eintrag>
        <Eintrag Key="4">vier</Eintrag>
    </Liste>;
}
Häufig verwendete Elemente können zur Optimierung in ein RenderFragment ausgelagert werden.

4. DOM Attribute

Man kann viele Attribute von HTML Elementen wunderbar über Blazor steuern. Nennenswerte Beispiele sind hidden und disabled, worüber man die Sichtbarkeit und die "Klickbarkeit" von Elementen steuern kann.

<input type="text" hidden="@isHidden" disabled="@isDisabled" />
 
@code {
    bool isHidden;
    bool isDisabled;
}

Fazit

Wer seinem Team wartbaren (selbsterklärend, überschaubar und sinnvoll abstrahiert) Programmcode bescheren möchte, sollte die Komplexität so gering wie möglich halten und nur mit guter Begründung Workarounds und Glue-Code einführen.

Absolut empfehlenswert für jeden Blazor-Anfänger ist das awesome-blazor GitHub Repositiory, kuriert von Adrien Torris. Hier findet man zu fast allen korrelierenden Themen ein lehrreiches Tutorial, die richtige Bibliothek oder das passende Referenzprojekt. Auch wenn der Begriff "Webentwicklung" gerne mit JavaScript und Co gleichgesetzt wurde, sollte man sich (in Blazor) von diesem Denkmuster verabschieden. Bevor man also selbst mit JavaScript interoperieren möchte, lohnt sich die Recherche ob nicht bereits jemand anderes diese Arbeit geleistet hat, auf die man aufbauen kann.

In diesem Sinne... Viel Spaß beim Blazen!


Wolfgang Lutz
Microsoft Certified Solution Developer
im agilen Scrum Team von K&K Software AG

Sie haben Fragen oder Interesse an einem Softwareprojekt mit Blazor? Rufen Sie uns einfach an – am besten gleich: 09382 – 31020!


Beitrag vom 29.04.2021 , aktualisiert am 02.05.2021

Kommentar abgeben: