Entity Framework 4 – Die neue Aufgabe der DataAnnotations ab der CTP 5
Validierungsmechanismen im .NET-Framework gibt es mittlerweile einige. Mit ASP.NET Dynamic Data kamen die DataAnnotations hinzu, die auch in ASP.NET MVC Einzug gehalten haben. Trotzdem ist es nicht immer einfach sich für den richtigen Validierungsmechanismus zu entscheiden, da in letzter Zeit mit jeder Neuerung bestehende Technologien gerne mal für Tod erklärt werden.
Bei der codezentrierten Anwendungsentwicklung mit dem DbContext bekommen sie auch eine neue Aufgabe. Im letzten Beitrag habe ich den Konfigurationsaufwand gezeigt, der notwendig war, um eine brauchbare Datenbank aus dem Klassenmodell abzuleiten. Wird dies nicht gemacht, wird zum Beispiel aus einer String-Variable ein nvarchar(4000)-Feld auf Datenbankebene, auch wenn die effektive Länge für die Eingabe nur 100 Zeichen umfassen soll. Das ist nicht der eleganteste Ansatz.
Innerhalb der DataAnnoations gibt es dafür das Validierungsattribut StringLength mit dem die Eingabeprüfung auf die Feldlänge möglich wird. Ab der CTP 5 haben diese DataAnnoations auch Einfluss auf dem Erstellungsprozess der Datenbank, es wird nur noch ein nvarchar(100)-Feld angelegt, wenn für die Eigenschaft das StringLength-Attribut mit der Länge 100 definiert wird. Diese Form führt zu einer enormen Erleichterung, da nun mit der Validierungslogik eine Angabe dieser Informationen in den Mappings nicht mehr notwendig ist. Genau gleich verhält es sich mit dem Required-Attribut.
Was mich bei der CTP 5 besonders überrascht hat, ist die Tatsache, dass die Datenbankerstellung mit den richtigen Längen auch funktioniert, wenn mit dem Attribut MetadataType gearbeitet wird und innerhalb einer Metadatenklasse die Attribute definiert werden. In meinen Validierungsbeispielen funktionierte das immer erst, wenn ich die Verknüpfung mittels dem TypeDescriptor hergestellt hatte.
Machen wir nun als ein kleines Beispiel:
Zuerst werden die Klassen erstellt, so wie es für die codezentrierte Anwendungsentwicklung üblich ist.
Customer-Klasse (C#)
[MetadataType(typeof(CustomerMetadata))]
public class Customer
{
public int Id { get; internal set; }
public string Name { get; set; }
public string Street { get; set; }
public string Postalcode { get; set; }
}
Metadaten-Klasse für Customer
public class CustomerMetadata
{
[StringLength(150)]
[Required]
public object Name { get; set; }
[StringLength(100, ErrorMessage = "The street name is too long.")]
[Required(AllowEmptyStrings = false, ErrorMessage = "The street name is required.")]
public object Street { get; set; }
[StringLength(5, ErrorMessage = "The postal code is too long.")]
[Required(AllowEmptyStrings = false, ErrorMessage = "The postal code is required.")]
[RegularExpression("^[0-9]{4,5}$", ErrorMessage = "The postal code is not valid.")]
public object Postalcode { get; set; }
}
Anschliessend kann diese Klasse dem Kontext hinzugefügt werden.
DbContext (C#)
public class TestContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
In dieser Kontext-Definition befindet sich auch keine Angabe zur Verbindungszeichenfolge zur Datenbank. Das liegt unter anderem daran, dass bedingt durch die Konventionen zuerst nach einem Eintrag mit dem Namen TestContext in der Konfigurationsdatei gesucht wird.
Verbindungszeichenfolge in Konfiguration App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="TestContext"
providerName="System.Data.SqlClient"
connectionString="Data Source=.;Initial Catalog=dbTest2;Integrated Security=True;MultipleActiveResultSets=true;" />
</connectionStrings>
</configuration>
Wird über die Kontext-Instanz-Eigenschaft Database die Methode CreateDatabaseIfNotExists aufgerufen, wird die Datenbank erstellt und enthält anschliessend eine Tabelle Customers. Die Abbildung zeigt dabei, dass die Attributdefinitionen bis auf die RegularExpression-Angabe auf die Tabelle angewendet worden sind.

- Abbildung 1 Angelegte Tabelle auf der Datenbank mit den Längen- und Pflichtangaben der DataAnnotations-Definitionen
Die Kontext-Instanz stellt neu auch eine Methode GetValidationErrors zur Verfügung die eine Überprüfung der geänderten Objekte ermöglicht. Die Methode SaveChanges wirft zudem eine Exception, falls die Regeln nicht erfüllt werden. Dieser auf Ausnahmen basierte Ansatz wird zwar häufig kritisiert, ist aber aus Sicht des Secure Codings durchaus sinnvoll, da bei einem Fehler die Verarbeitung abgebrochen wird und die Daten nicht zum Datenbankserver gesendet werden.
Ein Schwachpunkt sollte aber auch noch erwähnt werden:
- Es werden nicht alle Attribute unterstützt, was einen Mix aus DataAnnotations und Mapping zur Folge haben kann. Hier wäre es auch schön, wenn aus den Attributen, die auf reguläre Ausdrücke basieren, auf Datenbankebene die notwendigen CHECK-Constraints erstellt werden würden. Dies hätte schliesslich positive Auswirkungen auf die Datenintegrität. Wer diese Funktionalität realisieren will, muss jedoch auf dem SQL-Server zuerst eine CLR-Funktion für reguläre Ausdrücke erstellen.
Zum Schluss: Wer tendenziell eine Abneigung gegenüber den DataAnnotations hat, kann diesen Mechanismus über die Configuration-Eigenschaft deaktivieren. In meinen Augen wäre es schön, wenn dieser Ansatz auch beim ObjektContext Einzug halten würde.
- 0 Kommentar(e)


Mein Kommentar