Neu in .NET 8.0 [9]: Neue und erweiterte Datenannotationen

In .NET 8.0 hat Microsoft die Annotationsklassen im Namensraum System.ComponentModel.DataAnnotations erweitert.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen

(Bild: Pincasso/Shutterstock.com)

Lesezeit: 2 Min.
Von
  • Dr. Holger Schwichtenberg

Die Datenannotation im Namensraum System.ComponentModel.DataAnnotations gibt es schon seit dem klassischen .NET Framework. Die ersten wie [RegularExpression], [Range] oder [DataType] wurden in .NET Framework 3.5 eingeführt. Weitere Datenannotation folgten in .NET Framework 4.0 (z. B. [Display] und [Editable]) und .NET Framework 4.5 (z. B. [MaxLength], [Compare], [CreditCard] und [Phone]). Diese Datenannotation lassen sich auf Klassen oder Properties beziehungsweise Fields verwenden. Sie dienen der Spezifikation der Darstellung (z. B. [Display] und [Editable]) oder der Validierung von Werten (z.B. [RegularExpression], [Compare], [Range], [Phone] und [CreditCard]). GUI-Steuerelemente müssen die entsprechenden Annotationen berücksichtigen.

Es gibt nur wenige Steuerelemente, die die Darstellungsannotationen berücksichtigen – meist schaffen sich .NET-Entwicklerinnen und -Entwickler hier selbst Steuerelemente, die auf Basis der in den Annotationen abgelegten Metadaten eine grafische Benutzeroberfläche on the fly zur Laufzeit erzeugen. Bei den Validierungsannotationen gibt es mehr Steuerelemente, die die Annotationen berücksichtigen, darunter <InputText>, <InputNumber> und <InputDate> in Blazor. Eine Validierung kann man auch unabhängig von einem Steuerelement im Programmcode ausführen. Dazu gibt es die Klassen ValidationContext, Validator, ValidationResult und ValidationException.

Die [Range]-Annotation besitzt in .NET 8.0 die zusätzlichen Eigenschaften MinimumIsExclusive und MaximumIsExclusive, um die genannte Unter- und Obergrenze selbst als gültigen Werte auszuschließen. Standard ist wie bisher, dass die Grenzen eingeschlossen sind.

Zudem hat Microsoft drei neue Annotationsklassen ergänzt:

  • Die neue Annotation [Length] kann verwendet werden, um die Mindest- und Maximallänge von Objektmengen und Zeichenketten zu setzen.
  • Mit der neuen Annotation [Base64String] prüft man, ob eine Zeichenkette Base64-codiert ist.
  • Mit [AllowedValues] und [DeniedValues] kann man eine Liste erlaubter Werte für Properties eines Objekts angeben. Das funktioniert allerdings nur den Werten in einzelnen Properties oder Fields; auf Objektmengen angewendet wirken diese beiden Annotationen leider nicht.

Das nächste Listing zeigt ein aussagekräftiges Beispiel zu allen neuen Annotationsfeatures anhand der Klasse SoftwareArchitect mit einigen annotierten Eigenschaften. Die Validierung wird durchgeführt mit den Klassen ValidationContext und Validator.

using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using ITVisions;
using NET8_Console.CustomAnnotations;

namespace NET8_Console.FCL_Annotations;
 
/// <summary>
/// Neue und erweiterte Annotationen zur Validierung
/// </summary>
public class SoftwareArchitect
{
 // Neu in .NET 8.0 --> Nicht als Anrede erlaubt:
 [DeniedValues("", "Dr.", "Prof. Dr.")] 
 public string? Anrede { get; set; }
 
 // Neu in .NET 8.0 --> Erlaubte Titel:
 [AllowedValues("", "Dr.", "Prof. Dr.")] 
 public string? Titel { get; set; }
 
 // funktioniert hier nicht, da es sich um eine Liste handelt:
 [AllowedValues("C#", "Java", "JavaScript")] 
 public List<string> Sprachen { get; set; } = new();
 
 // Neu in .NET 8.0: Unter- und Obergrenze für Zeichenkettenlänge:
 [Length(2, 50)] 
 public string? Name { get; set; }
 
 // Neu in .NET 8.0: Unter- und Obergrenze für Mengenlänge:
 [Length(0, 3)] 
 public List<string> Websites { get; set; } = new();
 
 // Neu in .NET 8.0:
 [Base64String] 
 public string? Token { get; set; }
 
 // MinimumIsExclusive und MaximumIsExclusive 
 // sind neu seit .NET 8.0
 // > 0 und < 120 statt >= und <=:
 [Range(0d, 120d, MinimumIsExclusive = true, 
        MaximumIsExclusive = true)] 
 public double Alter { get; set; }
 #endregion
}
 
public class FCL_Annotations_Demo
{
 
 public void Run()
 {
  CUI.H2(nameof(FCL_Annotations_Demo));
 
  // Das zu validierende Objekt erstellen
  SoftwareArchitect hs = new()
  {
   Name = "Holger Schwichtenberg",
   Titel = "Doktor",
   Anrede = "",
   Token = "unsinn"
  };
  hs.Websites.Add("www.IT-Visions.de");
  hs.Websites.Add("www.dotnet-doktor.de");
  hs.Websites.Add("www.entwickler-lexikon.de");
  hs.Websites.Add("www.dotnet7.de");
  hs.Websites.Add("www.dotnet8.de");
  hs.Sprachen.Add("C#");
  hs.Sprachen.Add("JavaScript");
  hs.Sprachen.Add("TypeScript");
 
  // Validierung vorbereiten
  var ctx = new ValidationContext(hs);
  var results = new List<ValidationResult>();
 
  // Validierung durchführen
  if (!Validator.TryValidateObject(hs, ctx, results, true))
  {
   CUI.Error($"{results.Count} errors validating the objects:");
   // Fehler ausgeben
   foreach (var validationResult in results)
   {
    CUI.LI(validationResult.ErrorMessage, ConsoleColor.Red);
   }
  }
  else
  {
   // Es war alles OK
   CUI.Success("Validation succeeded!");
  }
 }
}

Beim Ausführen des Programmcodes im obigen Listing mit .NET 8.0 kommt es zu sechs Validierungsfehlern

(Bild: Screenshot von Holger Schwichtenberg)

(rme)