Blazor-Entwicklung: Komponenten, die immer passen
Blazor-Anwendungen mit unterschiedlichen Schichtarchitekturen können gemeinsame Razor Components nutzen: Ein Fallbeispiel zeigt den Einsatz in der Praxis.
Entwickler und Entwicklerinnen können mithilfe einer Razor Class Library gemeinsame Razor Components zwischen allen vier Blazor-Varianten (Blazor WebAssembly, Blazor Server, Blazor Desktop und Blazor MAUI) einsetzen, wie schon der Artikel "Eine Blazor-App für alle Plattformen [1]“ gezeigt hat.
Blazor-Arten im Architekturvergleich
Zu den Unterschieden zwischen den vier Blazor-Arten gehört auch, dass sie verschiedene Schichtenarchitekturen (Bild 1 und 2) ermöglichen:
- Eine Blazor-WebAssembly-Anwendung ist immer eine 3-Tier-Anwendung, denn sie läuft in der Sandbox des Webbrowsers, die nur einen Zugriff auf HTTP(S)-basierte Webservices (unter anderem REST-APIs, andere WebAPIs, gRPC-Dienste oder SignalR-Dienste) erlaubt.
- Bei einer Blazor-Server-Anwendung läuft der C#-Programmcode hingegen auf dem Webserver ohne Sandbox. Von hier aus können Entwickler und Entwicklerinnen direkt beliebige Ressourcen wie Datenbankmanagementsysteme ansprechen, sofern sie vom Webserver aus erreichbar sind. Ein Zugriff auf Webservices ist auch möglich; sodass die Wahl zwischen 2-Tier- und 3-Tier-Architekturen besteht.
- Blazor-Desktop-Anwendungen laufen ohne Sandbox direkt im Benutzerkontext von Windows. Sie können ist auf alle Ressourcen zugreifen, die für den angemeldeten Benutzer erreichbar sind. Auch hier haben Entwickler die Wahl zwischen 2-Tier- und 3-Tier-Architekturen.
- Blazor MAUI läuft ebenfalls nicht in einer Sandbox. Allerdings sind auf den Mobilbetriebssystemen in der Regel keine Datenbanktreiber vorhanden. Üblich ist daher eine 3-Tier-Architektur; 2-Tier ist auf Mobilplattformen verpönt.
Gemeinsamkeit nur über Webservices?
Wer eine Blazor-Anwendung schreiben will, die in einer Razor Class Library läuft und von allen Blazor-Arten eingebunden werden kann (via Kopf-Projekten, siehe "Eine Blazor-App für alle Plattformen [2]“), muss den Zugriff auf die benötigten Ressourcen in allen Blazor-Arten ermöglichen. Ein erster Gedanke: Der gemeinsame Nenner zwischen allen Blazor-Arten ist eine 3-Tier-Anwendung. Wenn also Razor Components in der Razor Class Library immer per Webservice auf die Datenbank zugreifen, laufen sie in allen Blazor-Arten.
Das stimmt, ist aber die einzige Option. Wer in Blazor Server und Blazor Desktop mit Webservices auf einem Application Server arbeitet, obwohl er das Datenbankmanagementsystem auch direkt ansprechen könnte, handelt sich zusätzlichen Netzwerkverkehr ein, der die Anwendung zunächst verlangsamt. Ein Application Server kann dies gegebenenfalls durch gutes Caching wieder ausgleichen.
Hier soll aber ein Ansatz präsentiert werden, bei dem Blazor Server und Blazor Desktop die Datenbank direkt verwenden, während Blazor WebAssembly und Blazor MAUI per Webservice auf die gleiche Datenbank zugreifen. Dabei verwenden alle vier Blazor-Arten die gleichen Razor Components in einer gemeinsamen Razor Class Library.
Entwickler können dafür den Datenzugriff komplett in die Kopfprojekte verlagern und die Razor Components in der Razor Class Library auf das Rendering reduzieren. In diesem Beispiel können die Razor Components in der Razor Class Library Daten abrufen und speichern. Dazu wird eine Abstraktionsschicht für den Datenzugriff zwischen 2-Tier und 3-Tier eingezogen, die mit einem Trick sehr effizient ist.
Praxisnahes Fallbeispiel "MiracleList"
MiracleList ist ein praxisnahes Fallbeispiel einer Single-Page-Web-Application zur Aufgabenverwaltung (Bild 3), das der Autor dieses Beitrags in seinen Büchern zu Blazor [3] und Vue.js [4] verwendet. Das Fallbeispiel zeigt viele User Experience-Aspekte moderner Webanwendungen, beispielsweise Responsive Webdesign, modale Dialoge, Drag&Drag , Kontextmenüs, Push-Nachrichten mit Toast-Benachrichtigungen sowie Progressive Web Apps.
Es gibt vier Implementierungen des MiracleList-Frontends mit verschiedenen Blazor-Varianten:
- MiracleList_BW: MiracleList mit Blazor WebAssembly (3-Tier, mit Zugriff auf die Daten über einen WebAPI-basierten Application Server)
- MiracleList_BS: MiracleList mit Blazor Server (2-Tier, mit Direktzugriff auf die Datenbank)
- MiracleList_BD: MiracleList mit Blazor Desktop in einer WPF-Rahmenanwendung (2-Tier mit Direktzugriff auf die Datenbank)
- MiracleList_BM: MiracleList mit Blazor MAUI (3-Tier mit Zugriff auf die Daten über einen WebAPI-basierten Application Server)
Diese vier Blazor-Implementierungen der MiracleList teilen sich eine gemeinsame Benutzeroberfläche, die in einer Razor Class Library realisiert ist. Im Architekturschaubild in Bild 4 zeigt die oberste Reihe die vier Kopfprojekte für die vier Blazor-Arten. Hier befinden sich der Startcode und der Anwendungszustand sowie eine Authentifizierungsklasse. Darunter folgen die gemeinsame Razor Class Library "MLBlazorRCL"sowie die 2-Tier/3-Tier-Abstraktion.
Vier Kopfprojekte für vier Blazor-Arten
Alle wesentlichen Razor Components der Anwendung stecken in der Razor Class Library MLRazorRCL
. Für alle vier Blazor-Arten gibt es zusätzlich jeweils ein Kopfprojekt (siehe Bild 4), das diese Razor Class Library referenziert. In den Kopfprojekten gibt es jeweils folgende Inhalte:
- App.razor: Startkomponente der Blazor-Anwendung
- index.html beziehungsweise _Host.cshtml: HTML-Startseite, die die Blazor-Anwendung sowie (bei Bedarf) CSS- und eigene JavaScript-Dateien lädt
- AppState.cs: Eine Klasse, die komponentenübergreifende Daten verwaltet. Dazu gehören Konfigurationsdaten und der Anwendungszustand (insbesondere der angemeldete Benutzer).
- Ein Authentication State Provider für die Benutzeranmeldung und -abmeldung. MiracleList_BW und MiracleList_BM verwenden dabei eine gemeinsame Klasse
MLAuthenticationStateProvider3Tier
; ebenso verwenden MiracleList_BS und MiracleList_BD eine gemeinsame KlasseMLAuthenticationStateProvider2Tier
. Diese gemeinsamen Klassen sind bewusst nicht in einer weitere DLL ausgelagert (um nicht noch mehr Projekte erzeugen zu müssen), sondern das Code Sharing erfolgt durch Verlinkung in den Projektdateien (siehe<Compile Include="…" Link=…">
in der .csproj-Datei). - Optional können die Kopfprojekte zusätzliche Razor Components implementieren, die von den Razor Components in MLBlazorRCL an ausgewählten Stellen in die Oberfläche integriert werden. Das ist Export.razor. Diese Komponente realisiert einen Export von Aufgaben ins Dateisystem und das Generieren eines Microsoft Word-Dokuments bei MiracleList_BD und MiracleList_BM, wobei die Word-Dokumentengenerierung nur auf Windows möglich ist.
Abstraktionsschicht für 2-Tier/3-Tier
Grundkonzepte für eine Abstraktionsschicht zwischen einer 2-Tier-Architektur und einer 3-Tier-Architektur sind Interfaces und Dependency Injection. Dazu schreibt eine Entwicklerin oder ein Entwickler eine Schnittstellendefinition mit Operationen für alle Lese- und Schreibzugriffen auf die Datenbank beziehungsweise andere Ressourcen. Dann erzeugt sie auf dieser Basis zwei Implementierungen: eine, die direkt die Geschäftslogik verwendet und eine zweite, die die Geschäftslogik über Webservices aufruft. Die Schnittstelle liegt in einem Projekt, das vier Blazor-Frontends referenzieren. Zudem wird die jeweils notwendige Implementierung der Schnittstelle als Projekt referenziert. Anschließend erfolgt die Verbindung von Schnittstelle zur gewünschten Implementierung per Dependency Injection.
Die Implementierung dieser drei Typen (eine Schnittstelle und zwei Klassen) kann aufwendig sein. Hier soll ein Ansatz gezeigt werden, der sich als der einfachste Weg für die Realisierung der Abstraktion erwiesen hat, da dabei zwei der drei oben genannten Typen automatisch generiert werden. Dafür ist es Voraussetzung, dass die Webservices bereits erstellt wurden und Metadaten auf Basis der Open API Specification (OAS), alias Swagger, bereitstellen. Das MiracleList-Backend [5] stellt OAS-Metadaten zur Verfügung. Das Backend basiert auf ASP.NET Core. Dort ist die Bereitstellung von Metadaten sehr einfach möglich und kann bereits beim Erstellen eines WebAPIs-Projekts per Häkchen aktiviert werden.
Mit OAS-Metadaten können Entwickler eine Proxy-.NET-Klasse für den Zugriff auf das Backend generieren. Visual Studio stellt dafür im Projektast "Connected Services" über "Add Connected Service/Service Reference (OpenAPI, gRPC)" einen Codegenerator bereit. Dieser bietet allerdings kaum Einstellmöglichkeiten und damit auch keinen Einfluss auf die Codegenerierung. Daher sei hier das Werkzeug NSwag Studio von Rico Sutter [6] empfohlen (Bild 5). Hier können Entwicklerinnen und Entwickler beispielsweise Shared Contracts nutzen, also gemeinsame Assemblies zwischen Client und Server verwenden, sodass bei der Proxy-Generierung bekannte Typen nicht erneut erzeugt werden.
Als eine weitere Option bietet NSwagStudio an, eine Schnittstelle direkt für die Proxyklasse zu erzeugen. Im Beispiel der MiracleList erhält ein Programmierer so die im folgenden Listing gezeigte Schnittstellendefinition. Für die bessere Lesbarkeit hat die Redaktion die Listings 1 und 2 mit zusätzlichen Umbrüchen im Vergleich zum GitHub-Repository versehen. Dort auf GitHub [7] finden Leser und Leserinnen dann den kompletten Programmcode.
namespace MiracleList;
/// <summary>
/// Diese ist eine aus dem generierten MiracleListProxy
/// heraus erstellte Schnittstelle zur Abstraktion zwischen
/// MiracleListProxy (3-Tier) und MiracleListNoProxy (2-Tier).
/// </summary>
public interface IMiracleListProxy
{
Task<LoginInfo> LoginAsync(LoginInfo loginInfo);
Task<bool> LogoffAsync(string token);
Task<List<BO.Category>> CategorySetAsync(string mL_AuthToken);
Task<BO.SubTask> ChangeSubTaskAsync(BO.SubTask st,
string mL_AuthToken);
Task<BO.Task> ChangeTaskAsync(BO.Task t, string mL_AuthToken);
Task<BO.Category> CreateCategoryAsync(string name,
string mL_AuthToken);
Task<BO.Task> CreateTaskAsync(BO.Task t, string mL_AuthToken);
System.Threading.Tasks.Task DeleteCategoryAsync(int id,
string mL_AuthToken);
…
Task<bool> RemoveFileAsync(int id, string name,
string mL_AuthToken);
Task<IDictionary<string, FileInfoDTO>> FilelistAsync(int id,
string mL_AuthToken);
Task UploadAsync(int id, string mL_AuthToken, FileParameter file);
}
Listing 1 MiracleListProxy.cs
Die zugehörige generierte Implementierung der Proxyklasse für den Zugriff auf die Webservices ist sehr lang (rund 1600 Zeichen) und daher in nächsten Listing nur in einem kleinen, exemplarischen Ausschnitt für die WebAPI-Operation /CategorySet/{categoryid} wiedergegeben.
[System.CodeDom.Compiler.GeneratedCode("NSwag", …)]
public partial class MiracleListProxy : IMiracleListProxy
{
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings>
_settings;
public MiracleListProxy(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
_settings =
new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(() =>
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
});
}
…
/// <summary>Liste der Kategorien</summary>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public System.Threading.Tasks.Task<System.Collections.Generic.List<Category>>
CategorySetAsync(string mL_AuthToken)
{
return CategorySetAsync(mL_AuthToken,
System.Threading.CancellationToken.None);
}
/// <summary>Liste der Kategorien</summary>
/// <returns>Success</returns>
/// <param name="cancellationToken">A cancellation token that can be used
/// by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public async System.Threading.Tasks.Task<System.Collections.Generic.List<Category>>
CategorySetAsync(string mL_AuthToken,
System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/v2/CategorySet");
var client_ = _httpClient;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
if (mL_AuthToken != null)
request_.Headers.TryAddWithoutValidation(
"ML-AuthToken", ConvertToString(
mL_AuthToken, System.Globalization.CultureInfo.InvariantCulture));
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse
("application/json"));
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(
request_,
System.Net.Http.HttpCompletionOption.ResponseHeadersRead,
cancellationToken).ConfigureAwait(false);
try
{
var headers_ =
System.Linq.Enumerable.ToDictionary(response_.Headers,
h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = ((int)response_.StatusCode).ToString();
if (status_ == "200")
{
var objectResponse_ = await
ReadObjectResponseAsync<System.Collections.Generic.List<Category>>
(response_, headers_).ConfigureAwait(false);
return objectResponse_.Object;
}
else
if (status_ != "200" && status_ != "204")
{
var responseData_ = response_.Content == null ? null :
await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException(
"The HTTP status code of the response was not expected ("
+ (int)response_.StatusCode + ").",
(int)response_.StatusCode, responseData_, headers_, null);
}
return default(System.Collections.Generic.List<Category>);
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
}
}
…
}
Listing 2. MiracleListProxy.cs ist die generierte Proxyklasse für den Webservicezugriff
Auf dieser Basis kann er dann eine zweite Implementierung der Schnittstelle IMiracleListProxy
schaffen. Die Klasse heißt MiracleListNoProxy
. Dieser Name drückt aus, dass die Implementierung keinen Proxy für das WebAPI darstellt, sondern direkt die Manager-Klassen der Geschäftslogik im gleichen Prozess verwendet, wie im nächsten Listing zu sehen. Diese Klasse MiracleListNoProxy
kann in eine eigene Assembly verpackt werden, kann aber auch Teil der Geschäftslogikschicht sein.
In der Implementierung in diesem Listing wird das übergebene Token ohne vorherige Inhaltsprüfung in eine Zahl konvertiert. Die 2-Tier-Variante braucht kein Authentifizierungstoken. Entwickler und Entwicklerinnen können hier direkt die Ganzzahl-Primärschlüssel der Tabelle mit den Benutzerdaten verwenden. Sollte die Benutzerschnittstellensteuerung etwas anderes als eine Zahl übergeben, wäre das ein klarer Fehler, der zum Laufzeitfehler in der Anwendung führen sollte.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BO;
using MiracleList;
namespace BL;
public class MiracleListNoProxy : MiracleList.IMiracleListProxy
{
…
public Task<List<Category>> CategorySetAsync(string mL_AuthToken)
{
var bl = new CategoryManager(Int32.Parse(mL_AuthToken));
var r = bl.GetCategorySet();
return System.Threading.Tasks.Task.FromResult(r);
}
…
}
Listing 3. Dieser Ausschnitt aus MiracleListNoProxy.cs greift direkt auf die Geschäftslogik zu
In den MiracleList-Implementierungen wird jeweils die eine oder andere Implementierung per Dependency Injection injiziert:
services.AddScoped<MiracleListAPI.IMiracleListProxy, MiracleListAPI.MiracleListProxy>();
oder
services.AddScoped<IMiracleListProxy, MiracleListNoProxy>();
Alle Komponenten beziehen dann per Dependency Injection ein Objekt mit diesem Schnittstellentyp, entweder innerhalb der Razor-Datei mit:
@using MiracleList;
@inject IMiracleListProxy proxy
oder innerhalb der Code-Behind-Datei mit
[Inject] MiracleList.IMiracleListProxy proxy { get; set; } = null;
Danach können alle Razor Components im Projekt MLBlazorRCL das per Dependency Injection gelieferte Proxy-Objekt verwenden. Sie müssen nichts darüber wissen, ob tatsächlich eine Kommunikation über den Webservice oder ein direkter Datenbankzugriff erfolgt:
var loginResult = await proxy.LoginAsync(loginData);
if (String.IsNullOrEmpty(loginResult.Message)) // OK
{
var categorySet = await proxy.CategorySetAsync(loginResult.Token);…}
Der Programmcode der generierten HTTP-Client-Proxy-Klasse (circa 1600 Zeilen) sowie der "NoProxy"-Implementierung (circa 100 Zeilen) sind hier aufgrund der Länge nicht komplett abgedruckt. Er ist aber komplett auf GitHub zu finden [8].
Abstraktion der Authentifizierung
Ein Authentication State Provider ist ein Mechanismus von Blazor. Für einige Blazor-Mechanismen, wie zum Beispiel die Autorisierungsdirektive @attribute [Authorize]
und das rollenabhängige Rendering mit der Komponente <AuthorizeView>
, ist eine von AuthenticationStateProvider abgeleitete Klasse notwendig, die in der Startup-Klasse in der Methode ConfigureServices()
zu registrieren ist:
services.AddScoped<AuthenticationStateProvider, MeinAuthenticationStateProvider>();
In der Implementierung des Authentication State Provider sind zwei Dinge wichtig: Die Klasse muss die Methode async Task<AuthenticationState> GetAuthenticationStateAsync()
überschreiben und in dem zurückgelieferten AuthenticationState die Blazor-Infrastruktur auf deren Anfrage jederzeit über den aktuellen Status (welche Benutzer angemeldet sind oder dass kein Benutzer angemeldet ist) informieren. Zudem muss die Klasse nach der erfolgreichen Benutzeranmeldung beziehungsweise bei einer Benutzerabmeldung die Blazor-Infrastruktur über einen Aufruf von NotifyAuthenticationStateChanged()
unter Angabe eines AuthenticationState-Objekts aktiv benachrichtigen.
In MiracleList wird die eigene Implementierung von Authentication State Provider darüber hinaus ebenfalls für weitere Mechanismen genutzt: An- und Abmeldung von Benutzer und Benutzerinnen, Wechsel des Backends, Prüfung der Verfügbarkeit eines Backends. Dafür gibt es bei MiracleList eine Schnittstelle IMLAuthenticationStateProvider, die zusätzliche Methoden vorsieht; die im folgenden Listing zu sehen sind.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;
using MiracleList;
namespace MiracleList;
public interface IMLAuthenticationStateProvider
{
/// <summary>
/// Ermittelt den aktuellen Anmeldezustand
/// </summary>
Task<AuthenticationState> GetAuthenticationStateAsync();
/// <summary>
/// Legt das aktuelle Backend fest
/// </summary>
/// <param name="backend">URL oder Connection String</param>
Task SetCurrentBackend(string backend);
/// <summary>
/// Prüft, ob das Backend verfügbar ist
/// </summary>
/// <param name="backend">URL oder Connection String</param>
Task<BackendState> CheckBackend(string backend);
/// <summary>
/// Benutzer anmelden
/// </summary>
Task<LoginInfo> LogIn(string username, string password, string backend);
/// <summary>
/// Benutzer abmelden
/// </summary>
Task Logout();
Listing 4. MiracleList_Interfaces/IMLAuthenticationStateProvider.cs
Von dieser Schnittstelle IMLAuthenticationStateProvider
gibt es dann zwei Implementierungen:
MLAuthenticationStateProvider2Tier
für Blazor Server und Blazor DesktopMLAuthenticationStateProvider3Tier
für Blazor WebAssembly und Blazor MAUI
Beide diese Klassen erben von der in Blazor integrierten Basisklasse AuthenticationStateProvider
und implementieren zusätzlich die eigene Schnittstelle IMLAuthenticationStateProvider
:
public class MLAuthenticationStateProvider3Tier : AuthenticationStateProvider, IMLAuthenticationStateProvider {
…}
Gemeinsame Razor-Komponenten im Projekt
Die Razor Class Library MLBlazorRCL
realisiert alle wesentlichen Razor Components für das MiracleList-Fallbeispiel sodass diese Komponenten in allen Blazor-Varianten einsetzbar (Blazor Server, Blazor WebAssembly. Blazor Desktop und Blazor MAUI) sind. Bild 6 zeigt, wie der Hauptbildschirm von MiracleList in verschiedene Komponenten aufgeteilt ist.
Gemeinsame Blazor-Komponenten für alle Ansichten:
MainLayout.razor
ist die Masterpage, die die obere weiße Leiste mit Logo (links) und Hamburger-Menü (rechts) darstellt. Sofern ein Benutzer angemeldet und der Bildschirm groß genug ist, zeigt die weiße Leiste in der Mitte auch Statusinformationen.ConnectionState.razor
: Komponente, die im Kopfbereich der Anwendung den Verbindungstatus darstellt
Gemeinsame Blazor-Komponenten für die Anmeldeansicht:
Login.razor
: Anmeldeformular, das bei erfolgreicher Anmeldung auf /main weiterleitetServerState.razor
: Ein Teil des Anmeldeformulars, der die Erreichbarkeit in der appsettings.json erfassten Backend-Systeme anzeigt.
Gemeinsame Blazor-Komponenten für die Hauptansicht:
Main.razor
: Hauptansicht /main beinhaltetAufgabenkategorieliste und Aufgabenliste mit Anlegen und Löschen von Kategorien und Aufgaben sowie Drag&Drop von Aufgaben auf KategorienTaskElement.razor
: Darstellung einer einzelnen Aufgabe innerhalb der Komponente Main.razorTaskEdit.razor[code]: Bearbeitungsformular für eine Aufgabe mit Validierung der Eingaben
- [code]SubTaskList.razor: Liste der Unteraufgaben einer Aufgabe mit Anlegen und Löschen von Unteraufgaben
TaskGrid.razor
: Bearbeitung der Aufgaben in einem Datagrid (von Radzen, kostenfrei).
Außerdem beinhaltet die [code]MLBlazorRCL[/CODE] gemeinsame Grafiken und Styles (.css), wie sie in Bild 7 zu sehen sind:
Fazit
Der Beitrag "Eine Blazor-App für alle Plattformen [9]“ hat gezeigt, wie Entwickler und Entwicklerinnen grundsätzlich gemeinsame Razor Components und statische Webartefakte in mehreren Blazor-Anwendungen nutzen können, auch wenn diese auf verschiedenen Blazor-Arten basieren. In diesem Artikel ging es dann um die Abstraktion von Daten- und Ressourcenzugriffen von der Schichtenarchitektur. Mit dem hier gezeigten Weg ist es – mit viel Codegenerierung – auf effiziente Weise möglich, Razor Components zu schreiben, die wahlweise Daten sowohl direkt von einem Datenbankmanagementsystem (2-Tier-Architektur) als auch via Webservice (3-Tier-Architektur).
Dr. Holger Schwichtenberg
ist Chief Technology Expert bei MAXIMAGO, die Innovations- und Experience-getriebener Softwareentwicklung, unter anderem in hochkritischen sicherheitstechnischen Bereichen, anbietet. Zudem ist er Leiter des Expertennetzwerks www.IT-Visions.de, das mit 43 Experten zahlreiche mittlere und große Unternehmen durch Beratung und Schulung bei der Entwicklung sowie dem Betrieb von Software unterstützt.
(fms [10])
URL dieses Artikels:
https://www.heise.de/-9297727
Links in diesem Artikel:
[1] https://www.heise.de/hintergrund/Web-Frontend-Framework-Eine-Blazor-App-fuer-alle-Plattformen-Teil-1-8987210.html
[2] https://www.heise.de/hintergrund/Web-Frontend-Framework-Eine-Blazor-App-fuer-alle-Plattformen-Teil-1-8987210.html
[3] https://www.it-visions.de/BlazorBuch
[4] https://www.it-visions.de/VueBuch
[5] http://miraclelistbackend.azurewebsites.net/
[6] https://github.com/RSuter/NSwag/wiki/NSwagStudio
[7] https://github.com/HSchwichtenberg/MiracleListNET
[8] https://github.com/HSchwichtenberg/MiracleListNET
[9] https://www.heise.de/hintergrund/Web-Frontend-Framework-Eine-Blazor-App-fuer-alle-Plattformen-Teil-1-8987210.html
[10] mailto:fms@heise.de
Copyright © 2023 Heise Medien