.NET 8.0 Preview 7 bringt Auto-Modus für Blazor

In der letzten Preview 7 von .NET 8.0 kann das .NET-basierte Webfrontend-Framework Blazor nun nahtlos zwischen Blazor Server und Blazor WebAssembly umschalten.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen

(Bild: Pincasso/Shutterstock)

Lesezeit: 11 Min.
Von
  • Dr. Holger Schwichtenberg
Inhaltsverzeichnis

Mit dem neu eingeführten Auto-Modus für das Blazor-Komponentenrendering liefert Microsoft in .NET 8.0 Preview 7 einen weiteren wesentlichen Baustein für das am 24. Januar 2023 angekündigte Blazor United. Die vorherige Preview-Version 6 von Anfang Juli hat bereits erlaubt, für Blazor Server- und Blazor WebAssembly-entwickelte Komponenten in einer Single-Page-Web-Application zu mischen.

Nun kann man eine Komponente nicht nur mit

<Counter IncrementAmount="1" @rendermode="@RenderMode.Server" />

und

<Counter IncrementAmount="1" @rendermode="@RenderMode.WebAssembly" />​

sondern auch via

<Counter IncrementAmount="1" @rendermode="@RenderMode.Auto" />

einbinden. Alternativ kann eine Blazor-Komponente den Auto-Modus statisch in ihrer Definition selbst festlegen:

@attribute [RenderModeAuto]

Der Auto-Modus verwendet WebAssembly-basiertes Rendering, falls die Runtime von Blazor WebAssembly innerhalb von 100 Millisekunden geladen werden kann. Eine derart kurze Zeit kann nur in sehr schnellen Netzwerken erreicht werden, oder wenn die Laufzeitumgebung schon im Cache des Browsers liegt. Falls Blazor diese Zeitspanne als nicht erreichbar ansieht, wird die Komponente zunächst per Blazor Server gerendert und eine Websocket-Verbindung zwischen Browser und Webserver für die Interaktivität aufgebaut. Die Laufzeitumgebung lädt dann im Hintergrund nach (siehe Abbildung 1). Ein Wechsel zu Blazor WebAssembly erfolgt dann aber erst, wenn die Komponente neue initialisiert wird, beispielsweise durch einen zwischenzeitlichen Wechsel zu einer anderen Seite.

Die Komponente Counter.razor ist interaktiv via Websocket-Verbindung zum Blazor-Server-Prozess. Die Blazor-WebAssembly-Laufzeitumgebung wird im Hintergrund nachgeladen (Abb. 1).

Bisher gibt es für den Auto-Modus keine Projektvorlage. Interessierte finden ein Beispiel mit einer einzigen Komponente im Auto-Modus (Counter.razor) auf GitHub.

Blazor in .NET 8.0 bietet neben Blazor WebAssembly und Blazor Server auch rein serverseitiges Rendering an. Die in bisherigen Preview-Versionen schon mögliche serverseitige Formularbehandlung ist nun auch ohne die eingebaute Blazor-Komponente <EditForm> möglich mit dem Standard-HTML-Tag <form>. Ein Formular muss aber ab Preview 7 bei serverseitigem Rendering immer einen Namen besitzen, der bei <form> mit @formname festgelegt wird und bei <EditForm> mit FormName:

<form method="post" @formname="contact" @onsubmit="AddContact">

beziehungsweise

<EditForm FormName="contact" Model="Contact" method="post" OnSubmit="AddContact" >
Heise-Konferenz: betterCode() .NET 8.0

Mit .NET 8.0 erscheint die nächste Long-term-Support-Version. Am 21. November 2023 bietet die von Heise und www.IT-Visions.de präsentierte Online-Konferenz betterCode() .NET 8.0 das Rüstzeug, sich einen grundlegenden Überblick zu .NET 8.0 zu verschaffen. Die Expertenvorträge zeigen die Neuerungen in .NET 8.0, ASP.NET Core 8.0, Blazor 8.0, .NET MAUI, C# 12.0 und mehr. Vier ganztägige Workshops vermitteln Hands-on-Wissen.

Kaskadierende Werte können Entwicklerinnen und Entwickler nun im Startcode einer Blazor-Anwendung innerhalb der Program.cs registrieren, um sie als Zustand für alle Komponenten in einer Komponente bereitzustellen, wie folgender Code aus dem Developer-Blog bei Microsoft zeigt:

// Registers a fixed cascading value
services.AddCascadingValue(sp => new MyCascadedThing { Value = 123 });

// Registers a fixed cascading value by name
services.AddCascadingValue("thing", 
                           sp => new MyCascadedThing { Value = 123 });

// Registers a cascading value using CascadingValueSource<TValue>
services.AddCascadingValue(sp =>
{
  var thing = new MyCascadedThing { Value = 456 };
  var source = new CascadingValueSource<MyCascadedThing>(thing, 
                                                         isFixed: false);
  return source;
});

Für den Schutz gegen Angriffe nach dem Prinzip der Cross-Site Request Forgery (CSRF/XSRF) liefert Microsoft in ASP.NET Core ab Preview 7 eine neue Middleware, die sich im Startcode einer ASP.NET Core-Anwendung via

builder.Services.AddAntiforgery();

integrieren lässt. Dieser Aufruf aktiviert zunächst nur in der Verarbeitungspipeline das Feature IAntiforgeryValidationFeature. Ein auf ASP.NET Core aufbauendes Webframework (z.B. Blazor, WebAPI, MVC, Razor Pages) muss sodann ein Antiforgery-Token im Programmcode berücksichtigen. Der Blogeintrag zeigt Implementierungsbeispiele für ASP.NET Core Minimal APIs und Blazor (bei Verwendung von <EditForm>), lässt aber offen, wie der Implementierungsstatus für andere ASP.NET Core-basierte Webframeworks wie MVC und Razor Pages ist.

In .NET 8.0 Preview 4 hatte Microsoft für ASP.NET Core Identity, das in ASP.NET Core integrierte Benutzerverwaltungssystem, WebAPI-Endpunkte in Ergänzung zu der bestehenden, auf serverseitigem Rendering basierenden Weboberfläche eingeführt, um ASP.NET Core Identity besser in Single-Page-Web-Apps (mit JavaScript oder Blazor) zu integrieren. Bisher gab es aber nur die WebAPI-Endpunkte für die Benutzerregistrierung (/register) und die Benutzeranmeldung (/login). Seit Preview 7 gibt es weitere Endpunkte für das Bestätigen von Benutzerkonten via E-Mail (/confirmEmail und /resendConfirmationEmail) und das Zurücksetzen von Kennwörtern (/resendConfirmationEmail), das Aktualisieren des Tokens (/refresh), Zwei-Faktor-Authentifizierung (/account/2fa) sowie das Lesen und Aktualisierung von Profildaten (/account/info). Man aktiviert die WebAPI-Endpunkte für ASP.NET Core Identity in der Program.cs-Datei via AddApiEndpoints() nach AddIdentityCore<T>() aus einem Beispielprojekt auf GitHub.:

using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddAuthorizationBuilder();

builder.Services.AddDbContext<AppDbContext>(options => 
                                            options.UseInMemoryDatabase("AppDb"));

builder.Services.AddIdentityCore<MyUser>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddApiEndpoints();

// Learn more about configuring Swagger/OpenAPI 
// at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Adds /register, /login and /refresh endpoints
app.MapIdentityApi<MyUser>();

app.MapGet("/", (ClaimsPrincipal user) => 
  $"Hello {user.Identity!.Name}").RequireAuthorization();

if (app.Environment.IsDevelopment())
{
  app.UseSwagger();
  app.UseSwaggerUI();
}

app.Run();

class MyUser : IdentityUser { }

class AppDbContext : IdentityDbContext<MyUser>
{
  public AppDbContext(DbContextOptions<AppDbContext> options) 
    : base(options) { }
}

Die Klasse HttpClient kann nun auch mit HTTPS-basierten Proxies zusammenarbeiten.

Nach langen Jahren ohne neue Steuerelemente für die Windows Presentation Foundation (WPF) liefert Microsoft ab .NET 8.0 Preview 7 einen neuen Dialog für das Auswählen von Ordnern im Dateisystem, das aber nicht Microsoft, sondern das Community-Mitglied Jan Kučera umgesetzt hat.

OpenFolderDialog openFolderDialog = new OpenFolderDialog()
{
  Title = "Select folder to open ...",
  InitialDirectory = 
    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
};

string folderName = "";
if (openFolderDialog.ShowDialog() == true)
{
  folderName = openFolderDialog.FolderName;
}

In einem Blogeintrag erwähnt Microsoft beim JSON-Serializer Verbesserungen für System.Text.Json. Dazu gehört, dass die auf zu serialisierende Klassen anwendbare Annotation [JsonSourceGenerationOptions] nun alle Optionen bietet, die auch die Klasse JsonSerializerOptions beim imperativen Programmieren erlaubt. System.Text.Json kann nun auch die Zahlentypen Half, Int128 und UInt128 sowie die rohen Speichertypen Memory<T> und ReadOnlyMemory<T> serialisieren. Bei Letzteren entstehen im Fall von Memory<Byte> und ReadOnlyMemory<Byte> Base64-kodierte Zeichenketten:

JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 41, 42, 43 });

serialisiert zu

[41,42,43]

Andere Inhaltstypen werden als JSON-Array serialisiert.

JsonSerializer.Serialize<Memory<int>>(new int[] { 41, 42, 43 });

serialisiert zu

"KSor"

beziehungsweise

JsonSerializer.Serialize<Memory<string>>(new string[] { "41", "42", "43" });

zu

["41","42","43"]

Mit den Annotationen [JsonInclude] und [JsonConstructor] können Entwicklerinnen und Entwickler nun die Serialisierung nicht öffentlicher Mitglieder erzwingen. Für jedes nicht öffentliche Mitglied, das mit [JsonInclude] annotiert ist, muss es in dem mit [JsonConstructor] annotierten Konstruktor einen Parameter geben, damit der Wert bei der Deserialisierung gesetzt werden kann:

public class Person
{
 [JsonConstructor] 
 // ohne dies: 'Deserialization of types without a parameterless
 // constructor, a singular parameterized constructor, or a 
 // parameterized constructor annotated with 'JsonConstructorAttribute' 
 // is not supported. 
 internal Person(int id, string name, string website)
 {
  ID = id;
  Name = name;
  Website = website;
 }
 
 [JsonInclude] 
 // ohne dies: 'Each parameter in the deserialization constructor
 // on type 'FCL_JSON+Person' must bind to an object property or
 // field on deserialization. Each parameter name must match with 
 // a property or field on the object. Fields are only considered 
 // when 'JsonSerializerOptions.IncludeFields' is enabled. 
 // The match can be case-insensitive.'
 
 internal int ID { get; }
 
 public string Name { get; set; }
 
 [JsonInclude] 
 // ohne dies: 'Each parameter in the deserialization constructor
 // on type 'FCL_JSON+Person' must bind to an object property or 
 // field on deserialization. Each parameter name must match with
 // a property or field on the object. Fields are only considered 
 // when 'JsonSerializerOptions.IncludeFields' is enabled. 
 //The match can be case-insensitive.'
 private string Website { get; set; }
 
 public override string ToString()
 {
  return $"{this.ID}: {this.Name} ({this.Website})";
 }
}

…
// Serialisierung
var p1 = new Person(42, "Dr. Holger Schwichtenberg", "www.dotnet-doktor.de");
string json4 = JsonSerializer.Serialize(p1); // {"X":42}
Console.WriteLine(json4);

// Deserialisierung
var p2 = JsonSerializer.Deserialize<Person>(json4);
Console.WriteLine(p2);

Die Klasse JsonNode hat neue Methoden wie DeepClone() und DeepEquals() erhalten. Außerdem wird bei JsonArray nun IEnumerable angeboten, was Aufzählung mit foreach und Language Integrated Query (LINQ) ermöglicht:

JsonArray jsonArray = new JsonArray(40, 42, 43, 42);
IEnumerable<int> values = 
  jsonArray.GetValues<int>().Where(i => i == 42);
foreach (var v in values)
{
 Console.WriteLine(v);
}

Wie angekündigt konzentriert sich das Entwicklungsteam von .NET MAUI in .NET 8.0 auf Fehlerbehebungen. Der Blogeintrag listet fünf wesentliche auf. Einziges neues Feature in .NET MAUI 8.0 Preview 7 sind Tastatur-Shortcuts, die man nun per SetAccelerator() einem Menüeintrag zuweisen kann:

<MenuFlyoutItem x:Name="AddProductMenu"
            Text="Add Product"
            Command="{Binding AddProductCommand}"
        />
…
MenuItem.SetAccelerator(AddProductMenu, 
                        Accelerator.FromString("Ctrl+A"));

Im Hauptblogeintrag zu .NET 8.0 ist noch eine Verbesserung für internationalisierte mobile Anwendungen auf Apple-Betriebssystemen (iOS, tvOS und MacCatalyst) erwähnt: Eine neue Einstellung reduziert die Größe der länderspezifischen Daten um bis zu 34 Prozent:

<HybridGlobalization>true</HybridGlobalization>

Wie schon bei Preview 5 und 6 gibt es auch bei Preview 7 keinen Blogeintrag des Entwicklungsteams hinter Entity Framework Core. Auch der GitHub-Issue, in dem Entity Framework Core-Engineering Manager Arthur Vickers bisher im Abstand von zwei Wochen über den Fortschritt berichtete, ist seit dem 22. Juni 2023 ohne neue Nachricht. Gleichwohl gibt es eine neue Version "Preview 7" auf NuGet.org, aber keinerlei Dokumentation etwaiger Änderungen oder Verbesserungen.

Laut einer Tabelle im oben genannten Issue hat das Entwicklungsteam die meisten der kleineren Neuerungen in Entity Framework Core in den ersten Preview-Versionen bereits erledigt und arbeitet nun an einigen größeren Projekten. Dazu gehören:

  • Tree Shaking/Trimming sowie Ahead-of-Time-Kompilation für ADO.NET und Entity Framework Core
  • Der neue, schnellere Microsoft SQL Server-Datenbanktreiber für ADO.NET und Entity Framework Core mit dem Codenamen "Woodstar"
  • Das Mapping von Value Objects für Domain Driven Design
  • Verbesserte Werkzeuge für Database-First-Entwicklung (alias Reverse Engineering) in Visual Studio