Neuerungen in .NET 9.0 Preview 1 und 2 Blog

DOTNET-DOKTOR Holger Schwichtenberg spricht über Neuerungen und Änderungen IN .NET 9.0

Dr. Holger Schwichtenberg

Apr 22, 2024

Microsoft hat die Vorschaureihe zu .NET 9.0 gestartet und wie jedes Jahr berichtet der DOTNET-DOKTOR über die Entwicklung

.NET 9.0 soll im November 2024 als Nachfolger des im November 2023 als stabile Version veröffentlichen .NET 8.0 erscheinen. Als ungerade Versionsnummer wird .NET 9.0 wieder nur Standard Term Support (STS) für 18 Monate erhalten, also nur bis Mai 2026, während der Support für .NET 8.0 noch bis November 2026 läuft.

ZUM NEWSLETTER

Regelmäßig News zur Konferenz und der .NET-Community

.NET 9.0 Preview 1 ist am 13. Februar 2024 erschienen und am 12. März 2024 folgte Preview 2. Die Vorschauversionen stehen zum Download unter [1] bereit.

An der Grundstruktur (drei Runtimes, ein SDK) hat sich nichts geändert (Abb. 1) und auch die unterstützten Plattformen sind gleichgeblieben. Laut Abbildung 1 sind weiterhin die Sprachversionen C# 12.0, F# 8.0 und Visual Basic .NET 16.9 enthalten – neue Sprachfeatures gibt es demnach bisher noch nicht.

 

.NET-9.0-Downloads

Abb. 1: .NET-9.0-Downloads

 

Neuerungen im .NET 9.0 SDK

Das Befehlszeilenwerkzeug dotnet test konnte bisher automatisierte Tests für ein Projekt mit mehreren .NET-Versionen nacheinander ausführen (z. B. <TargetFrameworks>net8.0;net9.0</TargetFrameworks> in der Projektdatei des Testprojekts). Eine neue Funktion in .NET 9.0 Preview 2 ist, dass diese Ausführung für verschiedene .NET-Versionen nun parallel erfolgt (Abb. 2). Außerdem nutzt dotnet test den Terminal Logger, der in .NET 8.0 eingeführt wurde und eine übersichtlichere Darstellung bietet. Das hat insbesondere positive Auswirkungen auf die Anzeige der Testergebnisse sowohl während als auch nach der Testausführung (Abb. 3). Bei Bedarf kann die Parallelisierung verhindert werden, indem man die MSBuild-Eigenschaft TestTfmsInParallel auf false setzt.

 

Parallele Testausführung für mehrere .NET-Versionen

Abb. 2: Parallele Testausführung für mehrere .NET-Versionen

 

Übersichtlichere Testergebnisse

Abb. 3: Übersichtlichere Testergebnisse

 

Nutzer:innen von .NET-SDK-basierten Tools haben nun die Möglichkeit, mit der Option —allow-roll-forward ein Werkzeug auf einer neueren .NET-Hauptversion laufen zu lassen, falls die .NET-Laufzeitumgebung, für die das Werkzeug eigentlich kompiliert wurde, nicht auf dem lokalen System verfügbar ist. Diese neue Option kann sowohl bei der Installation mit dotnet tool install als auch beim Ausführen mit dotnet tool run verwendet werden. Es muss jedoch darauf hingewiesen werden, dass aufgrund von Breaking Changes zwischen .NET-Hauptversionen keine Garantie besteht, dass das Werkzeug ordnungsgemäß auf einer neueren .NET-Laufzeitumgebung funktioniert.

SIE LIEBEN .NET?

Entdecken Sie die BASTA! Tracks

Neuerungen in der .NET-Klassenbibliothek

In der Basisklassenbibliothek wurden neue LINQ-Operatoren CountBy() und Index() hinzugefügt. CountBy() vermeidet den bisher notwendigen Einsatz von GroupBy() in Fällen, in denen nach Häufigkeit gruppiert werden soll (Listing 1). Index() liefert für ein IEnumerable<T> ein Tupel mit dem Wert und dem laufenden Index (0 bis n) zurück (Listing 2). Abbildung 4 zeigt die Ausgabe, bei der die Zusatzbibliothek SpectreConsole für die Balkenausgabe zum Einsatz kommt [2].

 

Listing 1: Ermitteln der häufigsten Vorname mit CountBy()

#region ---------------- Demodaten erstellen
  int count = 10000;
  CUI.H3($"Generiere {count} Demodaten...");
  Randomizer.Seed = new Random(42); // für gleichbleibende Testdaten!
  var personFaker = new AutoFaker<Person>("de")
    .RuleFor(fake => fake.ID, fake => fake.Random.Int(0))
    .RuleFor(fake => fake.Givenname, fake => fake.Name.FirstName())
    .RuleFor(fake => fake.Surname, fake => fake.Name.LastName())
    .RuleFor(fake => fake.Birthday, fake => fake.Date.Past(100).Date);
  List<Person> personSet = new();
  for (int i = 0; i < count; i++)
  {
    var p = personFaker.Generate();
    personSet.Add(p);
  }
  Console.WriteLine("Anzahl Personen: " + personSet.Count);
#endregion

#region ---------------- Auswertung
  CUI.H3("Auswertung:");
  // bisherige Implementierung mit GroupBy() + Select()
  var groups1 = personSet
    .GroupBy(info => info.Givenname) // Gruppieren nach GivenName
    .Select(group => new // Zählen
    {
      Key = group.Key,
      Value = group.Count()
    });

  Console.WriteLine("10 häufigste Vornamen im Datenbestand:");
  var groups1top10 = groups1.OrderByDescending(x => x.Value).Take(10);
  // Ausgabe mit SpectreConsole
  AnsiConsole.Write(new BarChart()
    .Width(60)
    .AddItems(groups1top10, (item) => new BarChartItem(
      item.Key, item.Value, Color.Yellow)));

  // Neue Implementierung mit CountBy()
  var groups2 = personSet
    .CountBy(x => x.Givenname); // Gruppieren nach Häufigkeit des GivenName

  Console.WriteLine("10 häufigste Vornamen im Datenbestand:");
  var groups2top10 = groups2.OrderByDescending(x => x.Value).Take(10);
  // Ausgabe mit SpectreConsole
  AnsiConsole.Write(new BarChart()
    .Width(60)
    .AddItems(groups2top10, (item) => new BarChartItem(
      item.Key, item.Value, Color.Yellow)));

#endregion

 

Ausgabe der Ermittlung der häufigsten Vornamen mit CountBy()

Abb. 4: Ausgabe von Listing 1

 

Listing 2: Einsatz der neuen Methode Index()

Console.WriteLine("10 häufigste Vornamen im Datenbestand:");
// bisher schon möglich:
foreach ((int index, KeyValuePair<string, int> group) in groups2top10.Select((wort, index) => (index, wort)))
{
  CUI.LI($"Platz {index + 1}: Vorname {group.Key} kommt {group.Value} vor.");
}

Console.WriteLine("10 häufigste Vornamen im Datenbestand:");
// neu ab .NET 9.0:
foreach ((int index, KeyValuePair<string, int> group) in groups2top10.Index())
{
  CUI.LI($"Platz {index + 1}: Vorname {group.Key} kommt {group.Value} vor.");
}

 

Die PriorityQueue-Klasse, die in .NET 6.0 eingeführt wurde, verfügt nun über eine Remove()-Methode, mit der Elemente entfernt werden können, auch wenn sie nicht an der Reihe sind. Remove() erweitert als Parameter das zu entfernende Element. Remove() liefert drei Werte zurück: Als Rückgabewert einen Boolean-Wert, der anzeigt, ob das Element vorhanden war und entfernt werden konnte. Im zweiten und dritten Methodenparameter kommt via out das entfernte Element sowie seine Priorität zurück.

 

Listing 3: Einsatz der Methode Remove in der Klasse PriorityQueue

public class FCL_Collections
{
  public void Run()
  {
    CUI.H2(nameof(FCL_Collections));

    var q = new PriorityQueue<string, int>();
    q.Enqueue("www.dotnet-doktor.de", 20);
    q.Enqueue("www.dotnet8.de", 2);
    q.Enqueue("www.IT-Visions.de", 10);
    q.Enqueue("www.dotnet-lexikon.de", 30);
    q.Enqueue("www.dotnet9.de", 1);

    Console.WriteLine(q.Count);

    CUI.H3("Entferne vorhandenes Element");
    bool b1 = q.Remove("www.dotnet8.de", out string e1, out int priority1);
    if (b1) Console.WriteLine($"Element {e1} mit Priorität {priority1} wurde entfernt!");
    else Console.WriteLine("Element nicht gefunden");

    CUI.H3("Versuch, nicht vorhandenes Element zu entfernen");
    bool b2 = q.Remove("www.dotnet7.de", out string e2, out int priority2);
    if (b2) Console.WriteLine($"Element {e2} mit Priorität {priority2} wurde entfernt!");
    else Console.WriteLine("Element nicht gefunden");

    CUI.H3($"Alle Elemente {q.Count} auflisten"); // 4
    var count = q.Count;
    for (int i = 0; i < count; i++)
    {
      var current = q.Dequeue();
      Console.WriteLine(i + ": " + current);
    }
    Console.WriteLine($"Verbliebene Elemente: {q.Count}"); // 0
  }
}

 

Mit .NET 9.0 ist es nun auch wieder möglich, zur Laufzeit erzeugte Assemblies im Dateisystem zu persistieren, indem die Methode Save() in der Klasse AssemblyBuilder verwendet wird (Listing 4). Diese Funktion war bereits im klassischen .NET Framework verfügbar (siehe Dokumentation zu Save() unter [3]). Im modernen .NET konnten zur Laufzeit erzeugte Assemblies bisher nur im RAM gehalten werden.

 

Listing 4: Speichern einer dynamisch erzeugten Assembly im Dateisystem

public void CreateAndSaveAssembly()
{
  CUI.H2(nameof(CreateAndSaveAssembly));

  string assemblyPath = Path.Combine(System.AppContext.BaseDirectory, "Math.dll");

  AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(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
}

 

 

Die .NET-Debugger in Visual Studio und Visual Studio Code zeigen nun die Inhalte von Dictionary-Klassen während des Debuggings deutlich übersichtlicher an (Abb. 5).

 

Verbesserte Debugger-Ansicht von Dictionary-Klassen in .NET 9.0

Abb. 5: Verbesserte Debugger-Ansicht von Dictionary-Klassen in .NET 9.0 [4]

 

Microsoft liefert ab .NET 9.0 eine Implementierung des KECCAK Message Authentication Code (KMAC) des US-amerikanischen National Institute of Standards and Technology (NIST) für kryptographische Funktionen [5].

 

Verbesserungen in System.Text.Json 9.0

Die Version 9.0 der JSON-Bibliothek System.Text.Json ermöglicht es, die Einrückung in der JSON-Zeichenkette über die JsonSerializerOptions anzupassen. Hierfür wurden zwei neue Einstellungen eingeführt: IndentCharacter und IndentSize. Listing 5 zeigt ein Beispiel. Als IndentCharacter werden aber nur ein Leerzeichen ‘ ‘ oder ein Tabulator ‘\t’ unterstützt.

Durch die Verwendung von JsonSerializerOptions.Web (Listing 5) können Entwickler:innen nun auf schnelle Weise dieselben Einstellungen für die JSON-Serialisierung und -Deserialisierung wählen, die standardmäßig im ASP.NET-Core-Web-API verwendet werden.

Listing 5: Setzen von IndentCharacter und IndentSize für die JSON-Serialisierung mit System.Text.Json

internal class FCL9_JSON
{
  public void Run()
  {
    CUI.H2(nameof(FCL9_JSON));

    CUI.H3("IndentCharacter und IndentSize")

    var consultant = new Consultant() { ID = 42, FullName = "Holger Schwichtenberg", Salutation = "Dr.", PersonalWebsite = "www.dotnet-doktor.de" };

    var options = new JsonSerializerOptions
    {
      WriteIndented = true,
      IndentCharacter = ' ',
      IndentSize = 2,
    };
    var json1 = JsonSerializer.Serialize(consultant, options);

    Console.WriteLine(json1);
    //liefert:
    //{
    //  "Languages": [],
    //  "PersonalWebsite": "www.dotnet-doktor.de",
    //  "ID": 42,
    //  "FullName": "Holger Schwichtenberg",
    //  "Salutation": "Dr.",
    //  "Address": null
    //}

    CUI.H3("JsonSerializerOptions.Web");
    var json2 = JsonSerializer.Serialize(consultant, JsonSerializerOptions.Web);
    Console.WriteLine(json2);
    //liefert:
    //{"languages":[],"personalWebsite":"www.dotnet-doktor.de","id":42,"fullName":"Holger Schwichtenberg","salutation":"Dr.","address":null}
  }
}

 

Verbesserungen in Entity Framework Core 9.0

Der Object-relational Mapper Entity Framework Core hat in Preview 1 und 2 bereits eine Reihe kleinerer Verbesserungen implementiert. Verbesserung in der Modellerstellung sind:

 

  • Bei Schlüssel- und Indexspalten können nun für den Microsoft SQL Server Angaben zum Füllfaktor gemacht werden. Der Füllfaktor bestimmt den Prozentsatz des Platzes auf eine Seite in der Datenbankdatei, der mit Daten gefüllt werden soll, wobei der Rest auf jeder Seite als freier Platz für zukünftiges Wachstum reserviert wird. Der Füllfaktor kann nun in Entity Framework Core 9.0 via HasFillFactor() für einfache und zusammengesetzte Schlüsselspalten und Indexe gesetzt werden (Listing 6).
  • Bei der Erstellung von Autowerten mit Sequenzen kann nun das Caching via UseCache() und UseNoCache() konfiguriert werden, z. B.
modelBuilder.HasSequence<int>("NameDerSequence")
  .HasMin(10).HasMax(255000)
  .IsCyclic()
  .StartsAt(11).IncrementsBy(2)
  .UseCache(3);
  • Die Umwandlung einer normalen Tabelle in eine temporäre Tabelle ist nun mit einer stark verkürzten Befehlsfolge in den Schemamigrationen möglich. Siehe dazu [6].

 

Listing 6: Einsatz von HasFillFactor()

modelBuilder.Entity<User>()
  .HasKey(e => e.Id)
  .HasFillFactor(80);

modelBuilder.Entity<User>()
  .HasAlternateKey(e => new { e.Region, e.Ssn })
  .HasFillFactor(80);

modelBuilder.Entity<User>()
  .HasIndex(e => new { e.Name })
  .HasFillFactor(80);

modelBuilder.Entity<User>()
  .HasIndex(e => new { e.Region, e.Tag })
  .HasFillFactor(80);

 

Verbesserung bei der Übersetzung von LINQ in SQL sind:

 

  • Bei der Übersetzung von LINQ zu SQL fasst Entity Framework Core 9.0 nun die SQL-Befehle von verschachtelten LINQ-Abfragen zusammen (Listing 6, 7 und 8).
  • Entwicklerinnen und Entwickler können nun mit den .NET-Funktionen EF.Parameter() und EF.Constant() erzwingen, dass bei der Übersetzung von LINQ-Abfragen nach SQL ein Wert in der SQL-Abfrage als Parameter bzw. als Konstante übergeben wird. Bisher lag die Entscheidung für die Parametrisierung allein beim ORM. Nun ist sie beeinflussbar, um die Nutzung des Query-Cache des Datenbankmanagementsystems zu optimieren. EF.Constant() ist auch als ein Nachtrag zu .NET 8.0 verfügbar, der in Version 8.0.2 erschienen ist. Das ist ungewöhnlich, da Microsoft behauptet, beim modernen .NET dem Semantic Versioning zu folgen, welches bei neuen Funktionen eine Änderung der Versionsnummer an der zweiten Stelle vorsieht.
  • Bei der Übersetzung von Abfragen, die sich auf JSON-Spalten beziehen, werden nun bei OPENJSON…WITH nur noch die Teile der JSON-Zeichenkette ausgewertet, die für die Bedingung oder das Resultset notwendig sind.
  • Falls das Zieldatenbanksystem ein Microsoft SQL Server der neusten Version 2022 ist, verwendet Entity Framework Core 9.0 nun die neu eingeführten T-SQL-Funktionen LEAST() und GREATEST() bei der Übersetzung von Min() und Math.Max().
  • Die Methode ExecuteUpdate() bietet nun eine verkürzte Syntax in Verbindung mit komplexen Typen:
var newAddress = new Address("www.IT-Visions.de", 45257, "Essen");
await context.Stores
  .Where(e => e.Region == "Germany")
  .ExecuteUpdateAsync(s => s.SetProperty(b => b.ShippingAddress, newAddress));
  • Analog zur bereits in Entity Framework Core 2.0 eingeführten Materialisierungsmethode ToHashSet() bietet der ORM nun ein asynchrones Pendant: ToHashSetAsync().

 

Weitere Verbesserungen in Entity Framework Core 9.0 sind:

 

  • Die Entity-Framework-Core-Kommandozeilenwerkzeuge sollen nun weniger häufig eine Neukompilierung des Projekts erfordern.
  • Die Erstellung von Konventionen für die Modellerstellung hat Microsoft verschlankt, hierfür gibt es ein umfangreiches Beispiel in der Dokumentation [7].
  • In der „What’s new“-Dokumentation findet man darüber hinaus einen Hinweis auf eine neue Überladung der Methode Parse() in der Klasse HierarchyId, die in Entity Framework Core 8.0 eingeführt wurde. Eine HierarchyId-Instanz soll man nicht nur wie bisher aus einer Zeichenkette (z. B. /4/1/3/1/2/), sondern auch typsicherer auf Basis einer anderen HierarchyId erstellen können, z. B.:
var daisy = await context.Halflings.SingleAsync(e => e.Name == "Daisy");
var child1 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1), "Toast");
var child2 = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 2), "Wills");
var child1b = new Halfling(HierarchyId.Parse(daisy.PathFromPatriarch, 1, 5), "Toast");
  • Allerdings funktioniert das in Entity Framework Core 9.0 Preview 2 noch nicht, sondern kommt dann voraussichtlich in Preview 3.

 

Listing 7: Verschachtelte LINQ-Abfragen

var dotnetPosts = context
  .Posts
  .Where(p => p.Title.Contains(".NET"));

var results = dotnetPosts
  .Where(p => p.Id > 2)
  .Select(p => new { Post = p, TotalCount = dotnetPosts.Count() })
  .Skip(2).Take(10)
  .ToArray();

 

 

Listing 8: Übersetzung der LINQ-Abfragen in Listing 1 in zwei SQL-Abfragen in Entity Framework Core 8.0

SELECT COUNT(*)
FROM [Posts] AS [p]
WHERE [p].[Title] LIKE N'%.NET%'

SELECT [p].[Id], [p].[Archived], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Discriminator], [p].[PublishedOn], [p].[Title], [p].[PromoText], [p].[Metadata]
FROM [Posts] AS [p]
WHERE [p].[Title] LIKE N'%.NET%' AND [p].[Id] > 2
ORDER BY (SELECT 1)
OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY

 

 

Listing 9: Übersetzung der LINQ-Abfragen in Listing 1 in eine einzige SQL-Abfrage in Entity Framework Core 9.0

SELECT [p].[Id], [p].[Archived], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Discriminator], [p].[PublishedOn], [p].[Title], [p].[PromoText], [p].[Metadata], (
  SELECT COUNT(*)
  FROM [Posts] AS [p0]
  WHERE [p0].[Title] LIKE N'%.NET%')
FROM [Posts] AS [p]
WHERE [p].[Title] LIKE N'%.NET%' AND [p].[Id] > 2
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY

 

Einige der Neuerungen in Entity Framework Core 9.0 Preview 1 und 2 stammen nicht von Microsoft-Entwickler:innen, sondern aus der Community, so zum Beispiel die Verbesserungen für Sequenzen, HasFillFactor(), ToHashSetAsync() und die weniger häufigen Kompilierungsvorgänge bei den Kommandozeilenwerkzeugen.

 

Verbesserungen für Blazor 9.0

In Webanwendung auf Basis von Blazor ist es jetzt möglich, Dependency Injection auch über den Konstruktor in einer Code-Behind-Datei in der Razor Component durchzuführen. Wie Listing 10 zeigt, können auch die in C# 12.0 eingeführten Primärkonstruktoren dafür verwendet werden.

 

Listing 10: Konstruktorinjektion in einer Code-Behind-Datei einer Blazor-Komponente

public partial class Counter(NavigationManager navigationManager, IJSRuntime js)
{
  private void NavigateHome() => navigationManager.NavigateTo("/");
  private string GetBlazorType
  {
    get
    {
      if (js.GetType().Name.Contains("Remote")) return "Blazor Server";
      if (js.GetType().Name.Contains("WebAssembly")) return "Blazor WebAssembly";
      return "Blazor Hybrid " + js.GetType().Name;
    }
  }
}

 

 

Bisher war es in Razor Components nur möglich, Dienste entweder durch @inject im Razor-Template oder durch die Annotation [Inject] auf einer Eigenschaft im Code zu injizieren.

Beim Interactive Server-side Rendering (alias Blazor Server) ist nun standardmäßig die Komprimierung in BlazorPack-Daten in der WebSockets-Verbindung aktiviert. Entwickler:innen können das jedoch bei Bedarf deaktivieren, indem sie DisableWebSocketCompression = true im Parameterobjekt von AddInteractiveServerRenderMode() setzen:

 

app.MapRazorComponents<App>()

  .AddInteractiveServerRenderMode(o => o.DisableWebSocketCompression = true);


Microsoft setzt nun standardmäßig die Content Security Policy (CSP) auf frame-ancestor: ‘self’. Das kann jedoch durch die Verwendung von ContentSecurityFrameAncestorsPolicy geändert werden:

 

app.MapRazorComponents<App>()
  .AddInteractiveServerRenderMode(o => o.ContentSecurityFrameAncestorsPolicy="'none'");

 

Weitere Neuerungen in ASP.NET Core 9.0a

In ASP.NET Core 9.0 SignalR ist Polymorphism für die Parameter von Operationen in ASP.NET-Core-SignalR-Hubs möglich, indem man die in System.Text.Json-Version 7.0 eingeführten Annotationen [JsonPolymorphicAttribute] und [JsonDerivedTypeAttribute] verwendet, die zu Metadaten (einem Type Discriminator) in JSON führen (z. B. “$type” : “PersonWithAge”). Listing 11 zeigt ein Einsatzbeispiel.

 

Listing 11: Polymorphisms bei ASP.NET Core SignalR

using System.Text.Json.Serialization;

namespace Blazor9;

[JsonPolymorphic]
[JsonDerivedType(typeof(PersonWithAge), nameof(PersonWithAge))]
[JsonDerivedType(typeof(PersonWithBirtday), nameof(PersonWithBirtday))]
public class PersonDTO
{
  public string? Name { get; set; }
}

public class PersonWithAge : PersonDTO
{
  public int Age { get; set; }
}

public class PersonWithBirtday : PersonDTO
{
  public DateTime Birthday { get; set; }
}

public class SignalRHub
{
  public int Search(PersonDTO person)
  {
    if (person is PersonWithAge)
    {
      // Suche Person mit Name und Alter
      // ...
    }
    else if (person is PersonWithBirtday)
    {
      // Suche Person mit Name und Geburtsdatum
      // ...
    }
    else
    {
      // Suche Person nur mit Name
      // ...
    }
    return 0; // ID = 0 -> nicht gefunden
  }
}


 

Bei der Verwendung von OAuth und OIDC ist es nun deutlich prägnanter, die Parameter anzupassen:

 

builder.Services.AddAuthentication().AddOpenIdConnect(options =>
  {
    options.AdditionalAuthorizationParameters.Add("prompt", "login");
    options.AdditionalAuthorizationParameters.Add("audience", "https://api.example.com");
});

Fast nichts Neues bei den GUIs

Bei .NET MAUI wurden bisher zahlreiche Fehlerbehebungen und kleinere Verbesserungen, wie beispielsweise die Unterstützung von animierten GIFs auf iOS, eingeführt. Die entsprechenden Release-Notes finden sich unter [8] und [9].

Informationen zu Verbesserungen bei Windows Forms oder der Windows Presentation Foundation (WPF) sind bisher nicht in den Release-Notes enthalten. Immerhin gab es jedoch im Februar ein Bekenntnis von Microsoft, weiterhin am Design für Windows Forms in Visual Studio zu arbeiten, wie unter [10] erklärt wird.

Geänderte Informationskanäle von Microsoft in der Kritik

In den letzten Jahren gab es zu jeder Vorschauversion des modernen .NET mehrere Blogeinträge im Microsoft .NET Blog [11]. Das galt bis einschließlich November 2023, dem Erscheinungstermin von .NET 8.0.

Zu den Previews von .NET 9.0 gibt es leider keine Blogeinträge mehr. Das ist Teil einer seit diesem Jahr veränderten Strategie bezüglich des .NET Blogs. Das .NET Blog soll sich laut einer Ankündigung von .NET Program Manager Rich Lander auf Beiträge über stabile .NET-Versionen konzentrieren [12].

Die Preview-Versionen will man jetzt stattdessen über einen neuen Diskussionsbereich auf GitHub anzukündigen [13]. Die einzelnen Neuerungen sind dann auch nicht dort, sondern in den verlinkten Release-Notes beschrieben, außerdem in „What’s New“-Dokumenten auf Microsoft Learn [14].

Allerdings sind die Neuerungen über mehrere Release-Notes-Dokumente verstreut und werden darin meist nur sehr knapp erwähnt. In den „What’s New“-Dokumenten zu .NET 9.0 [15], ASP.NET Core 9.0 [16], Entity Framework Core 9.0 [17] und .NET MAUI [18] sind die Neuerungen ausführlicher beschrieben. Allerdings fehlt in diesen Dokumenten die Trennung zwischen den verschiedenen Vorschauversionen und es finden sich in den Dokumenten (wie oben am Beispiel HierarchyId.Parse() gezeigt) auch Hinweise auf Features, die noch gar nicht in den aktuellen Preview-Versionen enthalten sind.

Microsofts neue Informationskanäle erschweren die Arbeiten derjenigen Entwicklerinnen und Entwickler, die am Puls der Zeit bleiben wollen und damit auch die Erstellung von journalistischen Fachberichten wie diesem. Die Kommentare auf GitHub [19] zeigen, dass die neue Strategie einigen Kundinnen und Kunden nicht gefällt: „confusing“, „badly done“ und „before it was even out was so much better“ heißt es in den Kommentaren. Ketzerische Reaktionen wie „so, is .net dead?“ gibt es auch. Mitarbeitende von Microsoft haben inzwischen mit Ankündigungen reagiert, die Darstellung zu überdenken. Jon Douglas schreibt: „I will make notes to ask our product and documentation teams to provide clear diffs“ [20].

Der einzige Blogbeitrag, der zu .NET 9.0 erschienen ist, trägt den Titel „Our Vision for .NET 9“ [21]. Ziele für .NET 9.0 sind demnach Verbesserungen für Cloud- und KI-Anwendungen, was angesichts der Microsoft-Unternehmensstrategie nicht überraschend ist. Welche Features genau dazu gehören, bleibt offen. Es bleibt bei schwammigen Aussagen wie „Entwickler werden großartige Bibliotheken und Dokumentationen finden, um mit OpenAI- und OSS-Modellen (gehostet und lokal) zu arbeiten. Wir werden weiterhin an der Zusammenarbeit an Semantic Kernel, OpenAI und Azure SDK arbeiten, um sicherzustellen, dass .NET-Entwickler beim Erstellen intelligenter Anwendungen eine erstklassige Erfahrung haben“.

Was schon zuvor angekündigt wurde, sind die Erweiterungen der Einsatzgebiete des Ahead-of-Timer-Compilers. Er funktioniert in .NET 7.0 und .NET 8.0 für Konsolenanwendungen, Minimal WebAPIs, gRPC-Dienste und Worker Services –, aber auch dort mit großen Einschränkungen (z. B. ist Entity Framework Core nicht nutzbar). Der Blogeintrag lässt offen, was konkret der AOT-Compiler in .NET 9.0 mehr können wird.

ZUM NEWSLETTER

Regelmäßig News zur Konferenz und der .NET-Community

Roadmaps und Backlogs

Auch die Liste der geplanten Features ist, anders als bei .NET 5.0 bis .NET 8.0, noch nicht sehr ausführlich. Am meisten hat derzeit das ASP.NET-Core-Team veröffentlicht (Abb. 6). Dazu gehören:

 

  • die bisher fehlende OAuth-Authentifizierung mit der Microsoft Identity Platform in der Projektvorlage Blazor Web App
  • eine einfachere Möglichkeit, den aktuellen Render-Modus bei Blazor zu ermitteln (hier muss man bisher die Typnamen der injizierten Instanzen von NavigationManager oder IJSRuntime abfragen)
  • Übergabe des Zustandes nach dem Pre-Rendering an den Client
  • Verbesserung der Multi-Threading-Unterstützung in Blazor WebAssembly
  • eine verbesserte Startzeit von Blazor-WebAssembly-Anwendungen, die im Browsercache liegen

 

Pläne für ASP.NET Core 9.0 und Blazor 9.0

Abb. 6: Pläne für ASP.NET Core 9.0 und Blazor 9.0 [22]

 

Auch das C#-Team gibt unter [23] wie immer frühe Einblicke. Wenig zu lesen gibt es hingegen bei .NET MAUI [24]. Nichts Aktuelles findet sich bei Windows Forms (Roadmap ist zwei Jahre alt, siehe [25]) und WPF (drei Jahre alte Roadmap, siehe [26]).

Das Entity Framework Core-Entwicklungsteam schreibt Stand 17. März 2024 nur „Coming soon“ [27]. Eine Linkliste auf verschiedene Roadmaps und Backlogs finden Sie unter [28].

 

Dr. Holger Schwichtenberg – alias der „DOTNET-DOKTOR“ – ist Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1 500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland. Seit 1998 ist er ununterbrochen Sprecher auf sämtlichen BASTA!-Konferenzen. Von Microsoft wird er seit 20 Jahren als MVP ausgezeichnet.

Mail: anfragen@IT-Visions.de

Twitter: @dotnetdoktor

Web: www.dotnet-doktor.de

 

Links und Literatur

[1] https://dotnet.microsoft.com/en-us/download/dotnet/9.0

[2] https://spectreconsole.net

[3] https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.assemblybuilder.save?view=netframework-4.8.1

[4] https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-8.0

[5] https://csrc.nist.gov/pubs/sp/800/185/final

[6] https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/whatsnew

[7] https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/whatsnew#make-existing-model-building-conventions-more-extensible

[8] https://github.com/dotnet/maui/releases/tag/9.0.100-preview.1.9973

[9] https://github.com/dotnet/maui/releases/tag/9.0.0-preview.2.10293

[10] https://devblogs.microsoft.com/dotnet/winforms-designer-64-bit-path-forward/

[11] https://devblogs.microsoft.com/dotnet/

[12] https://github.com/dotnet/core/discussions/9131

[13] https://github.com/dotnet/core/discussions

[14] https://learn.microsoft.com/en-us/dotnet/whats-new/

[15] https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-9/overview

[16] https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-8.0

[17] https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/whatsnew

[18] https://learn.microsoft.com/en-us/dotnet/maui/whats-new/dotnet-9?view=net-maui-8.0

[19] https://github.com/dotnet/core/discussions/9217

[20] https://github.com/dotnet/core/discussions/9217#discussioncomment-8765352

[21] https://devblogs.microsoft.com/dotnet/our-vision-for-dotnet-9/

[22] https://github.com/dotnet/aspnetcore/issues/51834

[23] https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md

[24] https://github.com/dotnet/maui/wiki/Roadmap

[25] https://github.com/dotnet/winforms/blob/main/docs/roadmap.md

[26] https://github.com/dotnet/wpf/blob/main/roadmap.md

[27] https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-9.0/plan

[28] https://github.com/dotnet/core/blob/main/roadmap.md

Top Articles About Blog

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, .NET, Git, C# & mehr

Agile & DevOps
Agile Methoden, wie Scrum oder Kanban und Tools wie Visual Studio, Azure DevOps usw.

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

JavaScript
Leichtegewichtig entwickeln

UI Technology
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud & Azure
Cloud-basierte & Native Apps