.NET 9.0 Preview 3: Vermischte Kleinigkeiten
Auch die dritte Vorschauversion auf .NET 9.0 enthält nur eine Sammlung kleinerer Neuerungen in den Basisklassen, ASP.NET Core und Entity Framework Core.

(Bild: Pincasso/Shutterstock.com)
- Dr. Holger Schwichtenberg
Microsoft hat .NET 9.0 Preview 3 veröffentlicht. Wie bereits in den ersten beiden Vorschauversionen auf .NET 9.0 – Preview 1 und Preview 2 – sind die Menge und die Tragweite der Neuerungen überschaubar, verglichen mit den großen Neuerungen, die es in den letzten Versionen von .NET auch schon in den ersten Vorschauversionen gab.
Immerhin hat Microsoft mittlerweile auf die Kritik der schlechten Kommunikation reagiert und gestaltet die Release Notes nun zielgerichteter (siehe Abbildung 1). Zu den früher etablierten Blogeinträgen im .NET-Blog ist Microsoft jedoch nicht zurückgekehrt.
(Bild:Â Dr. Holger Schwichtenberg)
Mehr Genauigkeit fĂĽr TimeSpan
Bei der Datenstruktur System.TimeSpan
, die es seit der ersten Version des .NET Framework aus dem Jahr 2002 gibt, war bisher eine Herausforderung, dass die Konvertierungsmethoden FromMicroseconds()
, FromSeconds()
, FromMinutes()
, FromHours()
und FromDays()
als Parameter einen double
-Wert erwarteten. Der double
-Typ ist als Fließkommazahl aber ungenau. Microsoft führt daher in .NET 9.0 zusätzlich neue Varianten der From
-Methoden ein, die Ganzzahlen als Parameter erhalten:
public static TimeSpan FromDays(int days);
public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0);
public static TimeSpan FromHours(int hours);
public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0);
public static TimeSpan FromMinutes(long minutes);
public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0);
public static TimeSpan FromSeconds(long seconds);
public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0);
public static TimeSpan FromMilliseconds(long milliseconds, long microseconds = 0);
public static TimeSpan FromMicroseconds(long microseconds);
Das folgende Beispiel zeigt die größere Genauigkeit der neuen Überladungen:
// bisher
TimeSpan timeSpan1 = TimeSpan.FromSeconds(value: 101.832);
Console.WriteLine($"timeSpan1 = {timeSpan1}"); // timeSpan1 = 00:01:41.8319999
// neu
TimeSpan timeSpan2 = TimeSpan.FromSeconds(seconds: 101, milliseconds: 832);
Console.WriteLine($"timeSpan2 = {timeSpan2}"); // timeSpan2 = 00:01:41.8320000
PersistedAssemblyBuilder anstelle von AssemblyBuilder
In .NET 9.0 Preview 1 hatte Microsoft die aus dem klassischen .NET Framework bekannte Möglichkeit wieder eingeführt, dynamisch zur Laufzeit erstellte Assemblies ins Dateisystem oder einen beliebigen Stream zu persistieren. In Preview 3 hat sich aber nun die API geändert. Anstelle der zuvor verwendeten Klasse AssemblyBuilder
AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(new AssemblyName("Math"), typeof(object).Assembly);
verwenden Entwicklerinnen und Entwickler nun die neue Klasse PersistedAssemblyBuilder
:
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("Math"), typeof(object).Assembly);
Das folgende Listing zeigt ein Beispiel zum Einsatz von PersistedAssemblyBuilder
. Ein darĂĽber hinausgehendes Beispiel zur Anpassung der Metadaten wie etwa dem Einstiegspunkt demonstriert Microsoft in den Release Notes.
string assemblyPath = Path.Combine(System.AppContext.BaseDirectory, "Math.dll");
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("Math"), typeof(object).Assembly);
TypeBuilder tb = ab.DefineDynamicModule("MathModule").DefineType("MathUtil", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder mb = tb.DefineMethod("Sum", MethodAttributes.Public | MethodAttributes.Static,
typeof(int), [typeof(int), typeof(int)]);
ILGenerator il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
tb.CreateType();
Console.WriteLine("Speichere Assembly unter: " + assemblyPath);
ab.Save(assemblyPath); // Speichern ins Dateisystem oder einen Stream
(Bild:Â Dmytro Vikarchuk/Shutterstock)
In der Online-Konferenz betterCode() .NET 9.0 am 19. November 2024 von iX und dpunkt.verlag werden .NET-Experten von www.IT-Visions.de den fertigen Stand von .NET 9.0 anhand von Praxisbeispielen präsentieren. Dazu zählen die Neuerungen bezüglich des .NET 9.0 SDK, C# 13.0, ASP.NET Core 9.0, Blazor 9.0, OR-Mapping mit Entity Framework Core 9.0, Windows Forms 9.0, WPF 9.0, WinUI, Cross-Plattform-Entwicklung mit .NET MAUI 9.0 und ein Ausblick auf .NET 10.0.
Der Ticketverkauf ist bereits gestartet: Vor Bekanntgabe des Programms sind vergünstigte Blind-Bird-Tickets erhältlich.
Schnellere Behandlung von Laufzeitfehlern
Dass .NET beim Behandeln von Laufzeitfehlern langsam ist, ist seit vielen Jahren bekannt. Daher gehört die Vermeidung von Laufzeitfehlern zu den Best Practices. Insbesondere sollte man Laufzeitfehler nicht als Ersatz für Kontrollflussanweisungen verwenden, etwa um bei ungültigen Werten eine Schleife oder Unterroutine zu verlassen. Microsoft hat aber laut eigener Aussage in den Release Notes zur .NET Runtime die Behandlung von Laufzeitfehlern um den Faktor zwei bis vier gesteigert. Das gilt für Windows x64, Windows ARM64, Linux x64 und Linux ARM64, aber nicht für 32-Bit-Windows. Einige verbliebene Fehler dabei sollen in Preview 4 behoben sein. Entwicklerinnen und Entwickler können via Umgebungsvariable
DOTNET_LegacyExceptionHandling = 1
oder Projektdateieinstellung
<RuntimeHostConfigurationOption Include="System.Runtime.LegacyExceptionHandling" Value="true" />
die Laufzeitumgebung dazu zwingen, das alte, langsamere Fehlerbehandlungsverfahren einzusetzen.
Verbesserte Ausgabe des Terminal Loggers
Der in .NET 8.0 eingeführte, prägnantere Terminal Logger (zum Beispiel bei dotnet build /tl
und msbuild /tl
) zeigt nun Fehlermeldungen mit ZeilenumbrĂĽchen besser an und zudem am Ende seiner Ausgabe eine Zusammenfassung der Anzahl der Fehler und Warnungen.
Was nicht in der Dokumentation steht, aber aus den Screenshots von Microsoft (siehe Abbildungen 2 und 3) hervorgeht und sich im Schnelltest bestätigte: Bei dotnet build
kann man nun auf den Parameter /tl
beziehunsgweise -tl
verzichten, um den Terminal Logger zu aktivieren. Bei MSBuild ist aber ohne diesen Parameter auch im .NET 9.0 SDK weiterhin der klassische Logger aktiv.
(Bild:Â Microsoft)
(Bild:Â Microsoft)
Typsichere Erstellung von Hierarchie-IDs
In Entity Framework Core 8.0, erschienen im November 2023, hat Microsoft die UnterstĂĽtzung fĂĽr den Microsoft-SQL-Server-Datentyp hierarchyid
eingefĂĽhrt, in Form der .NET-Klasse HierarchyId
. Seit Version 9.0 Preview 3 können Entwicklerinnen und Entwickler HierarchyId
-Instanzen nicht nur wie bisher aus einer Zeichenkette (zum Beispiel "/4/1/3/1/2/"), sondern auch typsicherer auf Basis einer anderen HierarchyId
und Ganzzahlen erstellen. Das folgende Listing zeigt den Einsatz der neuen Ăśberladungen von HierarchyId.Parse()
.
for (int j = 1; j < 5; j++)
{
// alt
var level2 = $"{startLevel}{i}/{j}/";
var hid2Alt = HierarchyId.Parse(level2);
// neu
var hid2Neu = HierarchyId.Parse(abteilungsleiter.Level, j);
var n = new Employee(hid2Neu, f.Name.FullName(), f.Random.Number(1970, 2023));
n.Company = company;
ctx.Add(n);
Console.WriteLine(" " + n.ToString());
var c3 = ctx.SaveChanges();
}
Automatisch kompilierte Modelle
Entity Framework Core erzeugt zur Laufzeit einigen Programmcode für das Mapping eines Objektmodells auf ein Datenbankschema. Diese Laufzeitcodegenerierung kann bei großen Objektmodellen zeitaufwendig sein. Zudem ist Laufzeitcodegenerierung nicht kompatibel mit dem Ahead-of-Time-Compiler Native AOT, den es seit .NET 7.0 gibt und den Microsoft in .NET 9.0 auch mit Entity Framework Core ermöglichen will.
Seit Version 6.0 des modernen objektrelationalen Mappers gibt es bereits kompilierte Modelle, bei denen ein Teil der Laufzeitcodegenerierung zur Entwicklungszeit stattfindet. Dazu musste man zuerst einen Kommandozeilenbefehl ausfĂĽhren
dotnet ef dbcontext optimize
beziehungsweise in der PowerShell:
Optimize-DbContext
Danach musste man im Programmcode noch einen Methodenaufruf in OnConfiguring()
ergänzen:
.UseModel(Kontextname.Instance)
Zudem mussten Entwicklerinnen und Entwickler die kompilierten Modelle an der Kommandozeile immer wieder neu erzeugen, wenn es Ă„nderungen am Objektmodell oder der Kontextklasse gab.
Alle diese Schritte können nun entfallen. Das Kompilieren lässt sich mit einem neuen MSBuild-Task <EFOptimizeContext>
automatisieren. Dazu muss man das neue NuGet-Paket Microsoft.EntityFrameworkCore.Tasks einbinden und dann in der Projektdatei die Einstellung <EFOptimizeContext>
auf true
setzen:
<PropertyGroup>
<EFOptimizeContext>true</EFOptimizeContext>
</PropertyGroup>
Bei jedem Ăśbersetzungsvorgang sieht man dann:
Optimizing DbContext...
Anpassungen der Modellkompilierung sind ĂĽber weitere Einstellungen wie EFStartupProject
, DbContextName
und EFTargetNamespace
möglich.
Der Aufruf UseModel()
ist seit Entity Framework Core 9.0 Preview 3 nicht mehr notwendig, sofern das kompilierte Modell in der gleichen Assembly liegt wie die Kontextklasse. Dies gilt unabhängig davon, ob man den MSBuild-Task einsetzt oder die Modellkompilierung weiterhin von Hand anstößt.
Verbesserungen fĂĽr ASP.NET Core und Blazor
ASP.NET Core kann zur Entwicklungszeit eine Fehlerseite anzeigen: app.UseDeveloperExceptionPage()
. Dieses Feature gibt es seit .NET Core 1.0. In .NET 9.0 wurde die Registerkarte Routing aber um weitere Informationen über den Endpunkt ergänzt (siehe Abbildung 4).
Die Klasse TypedResults
fĂĽr ASP.NET-Core-basierte WebAPIs kann nun auch den Statuscode 500 ("Internal Server Error") zurĂĽckliefern:
var app = WebApplication.Create();
app.MapGet("/", () => TypedResults.InternalServerError("Something went wrong!"));
app.Run();
Bei Blazor wurde die Klasse KeyboardEventArgs
um die Eigenschaft IsComposing
erweitert. Damit lässt sich abfragen, ob der Tastaturanschlag Teil einer zusammengesetzten Tastatureingabe ist, etwa bei Eingabe von Akzenten ("`" und "e" für "è").
(Bild:Â Dr. Holger Schwichtenberg)
Diskussionen ĂĽber den Fortschritt in .NET
.NET 9.0 enthält in den ersten drei Preview-Versionen wenig Neuerungen und auch die veröffentlichten Pläne von Microsoft fallen spärlich aus im Vergleich zu dem, was in den letzten Jahren kam (siehe beispielsweise die Roadmap für ASP.NET Core 9.0). Das Entity-Framework-Core-Team hat auch fünf Monate nach dem Erscheinen von .NET 8.0 und sieben Monate vor dem Erscheinen von .NET 9.0 immer noch keinen Plan für das kommende Release veröffentlicht (siehe Abbildung 5).
(Bild:Â Microsoft)
Interessanterweise kommt zudem keine der drei neuen Funktionen in ASP.NET Core 9.0 Preview 3 von Microsoft selbst, sondern alle dokumentierten Neuheiten in dieser Version sind Beiträge aus der Community.
Das alles bietet eine Steilvorlage fĂĽr diejenigen, die schon seit Jahren rufen: ".NET ist tot".
Man sollte allerdings bedenken, dass .NET 9.0 nur ein Standard-Term Release ist und man vielleicht daher weniger erwarten sollte als bei einem Long-Term Release wie .NET 8.0. Zudem findet vom 21. bis 23. Mai 2024 wieder Microsofts jährliche Entwicklerkonferenz Build statt, die bisher oft ein Aufhänger für die Bekanntgabe neuer Features war.
(mai)