Freigeben über


Datenüberprüfung

Hinweis

Nur ab EF4.1 – Die Features, APIs usw. auf dieser Seite wurden in Entity Framework 4.1 eingeführt. Wenn Sie eine frühere Version verwenden, gelten einige oder alle Informationen nicht.

Der Inhalt dieser Seite stammt aus einem Artikel, der ursprünglich von Julie Lerman (https://thedatafarm.com) geschrieben wurde.

Entity Framework bietet eine Vielzahl von Überprüfungsfeatures, die über eine Benutzeroberfläche für die clientseitige Überprüfung oder für die serverseitige Überprüfung verwendet werden können. Bei der ersten Verwendung von Code können Sie Überprüfungen mithilfe von Anmerkungs- oder Fluent-API-Konfigurationen angeben. Zusätzliche Überprüfungen und komplexere Überprüfungen können im Code angegeben werden und funktionieren unabhängig davon, ob Ihr Modell zuerst aus Code, Modell oder Datenbank stammt.

Das -Modell

Ich zeige die Überprüfungen mit einem einfachen Klassenpaar: Blog und Beitrag.

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string BloggerName { get; set; }
    public DateTime DateCreated { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime DateCreated { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public ICollection<Comment> Comments { get; set; }
}

Datenanmerkungen

Code First verwendet Anmerkungen aus der System.ComponentModel.DataAnnotations Assembly als ein Mittel zum Konfigurieren der ersten Codeklassen. Zu diesen Anmerkungen gehören diejenigen, die Regeln wie Required, MaxLength und MinLength bereitstellen. Eine Reihe von .NET-Clientanwendungen erkennen diese Anmerkungen auch, z. B. ASP.NET MVC. Sie können sowohl clientseitige als auch serverseitige Überprüfungen mit diesen Anmerkungen erzielen. Sie können beispielsweise erzwingen, dass die Eigenschaft "Blogtitel" eine erforderliche Eigenschaft ist.

[Required]
public string Title { get; set; }

Ohne zusätzlichen Code oder Markupänderungen in der Anwendung führt eine vorhandene MVC-Anwendung eine clientseitige Überprüfung durch, auch wenn eine Nachricht mithilfe der Eigenschaften- und Anmerkungsnamen dynamisch erstellt wird.

Abbildung 1

In der Postback-Methode dieser Create-Ansicht wird Entity Framework verwendet, um den neuen Blog in der Datenbank zu speichern, aber die clientseitige Überprüfung von MVC wird ausgelöst, bevor die Anwendung diesen Code erreicht.

Clientseitige Validierung ist jedoch nicht völlig zuverlässig. Benutzer können features ihres Browsers beeinträchtigen oder schlimmer noch, ein Hacker kann einige Tricks verwenden, um die UI-Überprüfungen zu vermeiden. Entitätsframework erkennt jedoch auch die Required Anmerkung und überprüft sie.

Eine einfache Möglichkeit zum Testen besteht darin, das clientseitige Überprüfungsfeature von MVC zu deaktivieren. Sie können dies in der web.config Datei der MVC-Anwendung tun. Der Abschnitt "appSettings" weist einen Schlüssel für "ClientValidationEnabled" auf. Wenn Sie diesen Schlüssel auf "false" festlegen, wird verhindert, dass die Benutzeroberfläche Überprüfungen durchführt.

<appSettings>
    <add key="ClientValidationEnabled"value="false"/>
    ...
</appSettings>

Auch wenn die clientseitige Überprüfung deaktiviert ist, erhalten Sie dieselbe Antwort in Ihrer Anwendung. Die Fehlermeldung "Das Titelfeld ist erforderlich" wird wie zuvor angezeigt. Nur dass es jetzt ein Ergebnis der serverseitigen Validierung ist. Entity Framework führt die Überprüfung der Required Anmerkung durch (bevor überhaupt ein INSERT-Befehl zum Senden an die Datenbank erstellt wird) und gibt den Fehler an MVC zurück, das die Meldung anzeigt.

Fluent-API

Sie können die Fluent-API von Code First anstelle von Anmerkungen verwenden, um sowohl die clientseitige als auch die serverseitige Validierung zu erreichen. Anstatt zu verwenden Required, zeige ich Ihnen dies mithilfe einer MaxLength-Überprüfung.

Fluent-API-Konfigurationen werden angewendet, während Code First das Modell aus den Klassen erstellt. Sie können die Konfigurationen einfügen, indem Sie die OnModelCreating-Methode der DbContext-Klasse überschreiben. Hier ist eine Konfiguration, die angibt, dass die BloggerName-Eigenschaft nicht länger als 10 Zeichen sein kann.

public class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().Property(p => p.BloggerName).HasMaxLength(10);
    }
}

Überprüfungsfehler, die basierend auf den Fluent-API-Konfigurationen ausgelöst werden, erreichen nicht automatisch die Benutzeroberfläche, aber Sie können sie im Code erfassen und dann entsprechend darauf reagieren.

Nachfolgend finden Sie einige Ausnahmebehandlungsfehlercode in der BlogController-Klasse der Anwendung, die diesen Überprüfungsfehler erfasst, wenn Entity Framework versucht, einen Blog mit einem BloggerName zu speichern, der das Maximum von 10 Zeichen überschreitet.

[HttpPost]
public ActionResult Edit(int id, Blog blog)
{
    try
    {
        db.Entry(blog).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch (DbEntityValidationException ex)
    {
        var error = ex.EntityValidationErrors.First().ValidationErrors.First();
        this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
        return View();
    }
}

Die Validierung wird nicht automatisch in die Ansicht zurückübergeben, weshalb zusätzlicher Code mit ModelState.AddModelError verwendet wird. Dadurch wird sichergestellt, dass die Fehlerdetails in die Ansicht gelangen, die dann den ValidationMessageFor Htmlhelper zum Anzeigen des Fehlers verwendet.

@Html.ValidationMessageFor(model => model.BloggerName)

IValidatable-Objekt

IValidatableObject ist eine Schnittstelle, die sich in System.ComponentModel.DataAnnotations befindet. Obwohl sie nicht Teil der Entity Framework-API ist, können Sie sie weiterhin für die serverseitige Überprüfung in Ihren Entity Framework-Klassen nutzen. IValidatableObject stellt eine Validate Methode bereit, die Entity Framework während SaveChanges aufruft, oder Sie können sie jederzeit selbst aufrufen, wenn Sie die Klassen validieren möchten.

Konfigurationen wie Required und MaxLength führen eine Validierung für ein einzelnes Feld durch. In der Validate Methode können Sie noch komplexere Logik haben, z. B. den Vergleich von zwei Feldern.

Im folgenden Beispiel wurde die Blog-Klasse erweitert, um IValidatableObject zu implementieren und dann eine Regel bereitzustellen, die Title und BloggerName nicht abgeglichen werden können.

public class Blog : IValidatableObject
{
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }

    public string BloggerName { get; set; }
    public DateTime DateCreated { get; set; }
    public virtual ICollection<Post> Posts { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Title == BloggerName)
        {
            yield return new ValidationResult(
                "Blog Title cannot match Blogger Name",
                new[] { nameof(Title), nameof(BloggerName) });
        }
    }
}

Der ValidationResult-Konstruktor verwendet eine string, die die Fehlermeldung darstellt, und ein Array von strings, die die Membernamen repräsentieren, die der Überprüfung zugeordnet sind. Da sowohl die Title-Eigenschaft als auch die BloggerName-Eigenschaft überprüft werden, werden beide Eigenschaftsnamen zurückgegeben.

Im Gegensatz zur von der Fluent-API bereitgestellten Überprüfung wird dieses Überprüfungsergebnis von der Ansicht erkannt und der Ausnahmehandler, den ich zuvor zum Hinzufügen des Fehlers ModelState verwendet habe, ist nicht erforderlich. Da ich beide Eigenschaftennamen in der ValidationResultDatei festgelegt habe, zeigt die MVC HtmlHelpers die Fehlermeldung für beide Eigenschaften an.

Abbildung 2

DbContext.ValidateEntity

DbContext hat eine überschreibbare Methode namens ValidateEntity. Wenn Sie SaveChangesEntity FrameworkSaveChanges aufrufen, wird diese Methode für jede Entität im Cache aufgerufen, deren Status nicht ist. Sie können die Validierungslogik direkt hier einfügen oder sogar diese Methode verwenden, um beispielsweise die Blog.Validate im vorherigen Abschnitt hinzugefügte Methode aufzurufen.

Hier ist ein Beispiel für eine ValidateEntity Überschreibung, die neue Post validiert, um sicherzustellen, dass der Beitragstitel nicht bereits verwendet wurde. Zunächst wird überprüft, ob es sich bei der Entität um einen Post handelt und ob deren Status "hinzugefügt" ist. Wenn dies der Fall ist, wird in der Datenbank nachschaut, ob bereits ein Beitrag mit demselben Titel vorhanden ist. Wenn bereits ein Beitrag vorhanden ist, wird ein neuer DbEntityValidationResult Beitrag erstellt.

DbEntityValidationResult beherbergt eine DbEntityEntry und eine ICollection<DbValidationErrors> für eine einzelne Entität. Zu Beginn dieser Methode wird eine DbEntityValidationResult instanziiert, und dann werden alle gefundenen Fehler zu der ValidationErrors-Auflistung hinzugefügt.

protected override DbEntityValidationResult ValidateEntity (
    System.Data.Entity.Infrastructure.DbEntityEntry entityEntry,
    IDictionary<object, object> items)
{
    var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());

    if (entityEntry.Entity is Post post && entityEntry.State == EntityState.Added)
    {
        // Check for uniqueness of post title
        if (Posts.Where(p => p.Title == post.Title).Any())
        {
            result.ValidationErrors.Add(
                    new System.Data.Entity.Validation.DbValidationError(
                        nameof(Title),
                        "Post title must be unique."));
        }
    }

    if (result.ValidationErrors.Count > 0)
    {
        return result;
    }
    else
    {
        return base.ValidateEntity(entityEntry, items);
    }
}

Explizites Auslösen der Überprüfung

Ein Aufruf von SaveChanges löst alle in diesem Artikel behandelten Überprüfungen aus. Sie müssen sich jedoch nicht darauf SaveChangesverlassen. Sie können es vorziehen, an anderer Stelle in Ihrer Anwendung zu überprüfen.

DbContext.GetValidationErrors wird alle Validierungen auslösen, einschließlich derjenigen, die durch Anmerkungen oder die Fluent-API definiert sind, der in IValidatableObject erstellten Validierung (z. B. Blog.Validate) und der in der DbContext.ValidateEntity-Methode durchgeführten Validierungen.

Der folgende Code ruft GetValidationErrors bei der aktuellen Instanz eines DbContext auf. ValidationErrors werden nach Entitätstyp gruppiert in DbEntityValidationResult. Der Code durchläuft zuerst die von der Methode zurückgegebenen DbEntityValidationResults und dann die einzelnen DbValidationErrors darin.

foreach (var validationResult in db.GetValidationErrors())
{
    foreach (var error in validationResult.ValidationErrors)
    {
        Debug.WriteLine(
            "Entity Property: {0}, Error {1}",
            error.PropertyName,
            error.ErrorMessage);
    }
}

Weitere Überlegungen bei der Verwendung der Validierung

Hier sind einige weitere Punkte, die Sie bei der Verwendung der Entity Framework-Überprüfung berücksichtigen sollten:

  • Lazy-Loading ist während der Validierung deaktiviert
  • EF überprüft Datenanmerkungen auf nicht zugeordneten Eigenschaften (Eigenschaften, die keiner Spalte in der Datenbank zugeordnet sind)
  • Die Überprüfung wird ausgeführt, nachdem Änderungen während SaveChanges erkannt wurden. Wenn Sie während der Überprüfung Änderungen vornehmen, liegt es in Ihrer Verantwortung, den Änderungs-Tracker zu benachrichtigen.
  • DbUnexpectedValidationException wird ausgelöst, wenn während der Überprüfung Fehler auftreten
  • Facets, die Entity Framework im Modell enthält (maximale Länge, erforderlich usw.), führen zu einer Überprüfung, auch wenn keine Datenanmerkungen in Ihren Klassen vorhanden sind und/oder Sie den EF Designer zum Erstellen ihres Modells verwendet haben
  • Rangfolgeregeln:
    • Fluent-API-Aufrufe überschreiben die entsprechenden Datenanmerkungen
  • Ausführungsreihenfolge:
    • Die Eigenschaftenüberprüfung erfolgt vor der Typüberprüfung.
    • Die Typüberprüfung tritt nur auf, wenn die Eigenschaftenüberprüfung erfolgreich ist.
  • Wenn eine Eigenschaft komplex ist, enthält ihre Überprüfung auch Folgendes:
    • Überprüfung auf Eigenschaftsebene für die komplexen Typeigenschaften
    • Validierung auf Typenebene für den komplexen Typ, einschließlich IValidatableObject Validierung für den komplexen Typ

Zusammenfassung

Die Validierungs-API in Entity Framework spielt sehr gut mit der clientseitigen Überprüfung in MVC ab, aber Sie müssen sich nicht auf die clientseitige Überprüfung verlassen. Entity Framework kümmert sich um die Validierung auf der Serverseite für DataAnnotations oder Konfigurationen, die Sie mit der Code-First-Methodik der Fluent API angewendet haben.

Außerdem haben Sie eine Reihe von Erweiterungspunkten zum Anpassen des Verhaltens gesehen, unabhängig davon, ob Sie die IValidatableObject Schnittstelle verwenden oder auf die DbContext.ValidateEntity Methode tippen. Und diese beiden letzten Methoden der Überprüfung sind über den DbContext-Workflow, sei es Code First, Model First oder Database First, zur Beschreibung Ihres konzeptionellen Modells verfügbar.