Montag, Juni 1, 2020

Entity Framework-Validierung mit teilweise Aktualisierungen

Bin ich mit Entity Framework 5.0 mit DbContext-und POCO-Entitäten. Es gibt eine einfache Einheit mit 3 Eigenschaften:

public class Record
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsActive { get; set; }
}

Dem Titel-Feld ist immer unverändert, und die UI zeigt einfach, ohne jedes Eingabefeld, um es zu ändern. Das ist, warum die Title Feld null wenn das Formular an den server gesendet wird.

Hier ist, wie ich sage EF zu führen partielles update des Unternehmens (IsActive Feld nur):

public class EFRepository<TEntity>
{
   ...
   public void PartialUpdate(TEntity entity, params Expression<Func<TEntity, object>>[] propsToUpdate)
   {
       dbSet.Attach(entity);
       var entry = _dbContext.Entry(entity);
       foreach(var prop in propsToUpdate)
           contextEntry.Property(prop).IsModified = true;
   }
}

und der Ruf:

repository.PartialUpdate(updatedRecord, r => r.IsActive);

Aufrufen SaveChanges Methode, bekomme ich die DbEntityValidationException, das mir sagt, Title erforderlich ist. Wenn ich dbContext.Configuration.ValidateOnSaveEnabled = false ist alles OK.
Gibt es eine Möglichkeit, vermeiden Sie das deaktivieren der überprüfung auf den gesamten Kontext und zu sagen, das EF nicht die Eigenschaften überprüft werden, die nicht aktualisiert wird?
Vielen Dank im Voraus.

  • Dies scheint wie eine Menge Arbeit, um etwas zu tun, das ist ganz einfach, befassen sich mit sonst. Sie gehören einfach einem versteckten Feld im Formular mit Ihrer read-only-Modell Elemente, dann sind Sie in dem update enthalten und EF macht seinen ändern, tracking-und kennt den Wert nicht geändert hat.
  • Was ist stub Personen als? Zum Beispiel, ich hab eine Action-Methode, die Sie markiert eine Person, die als gelöscht. Die folgenden code: var person = new Person { Id = 5 }; dbSet.Attach(person); dbSet.Entry(person).Property(p => p.IsDeleted).IsModified = true; dbContext.SaveChanges(); wird die Ursache die gleiche Ausnahme. Tut DbContext Validierung der Arbeit gut mit stub-Entitäten überhaupt? Ich möchte aviod abrufen der gesamten Entität aus der Datenbank nur für die Markierung als gelöscht.
  • Funktioniert es wirklich? Befestigen Sie wird verwendet, wenn Sie eine Entität in der hand und Sie sind sicher, dass diese Entität in der Datenbank existiert und ist identisch. Ordnen Sie eine Person, die sich von dem unterscheidet, was in der Filiale (der Titel ist anders). Dann Kennzeichnen Sie die entity als geändert (durch Markierung einer Eigenschaft geändert werden kann). Da EF arbeitet auf Personen und nicht auf Eigenschaften, die es aktualisieren wird, alle Eigenschaften und nicht nur die, die als geändert markiert. Mein mentales Modell von der EF sagt mir, dass der Title-Spalte in der Datenbank auf null gesetzt werden nach diesem. Können Sie überprüfen, wenn dies nicht der Fall ist?
  • Validierung standardmäßig überprüft, die alle Eigenschaften von Entitäten in der Zusätzlichen und Geänderten Zustand. Es ist möglich, dieses Verhalten zu ändern durch die Verwendung von Erweiterungsmechanismen ausgesetzt durch die Validierung. Beachten Sie, dass sobald Sie markieren Sie Ihr angeschlossenen Unternehmen als geändert es ist nicht mehr ein stub-Einrichtung, wie ich glaube, es wird an die Datenbank geschickt werden…
  • EF ermöglicht partielle updates der Einheit. Zum Beispiel dbSet.Attach(entity); dbContext.Entry(entity).State = EntityState.Modified; dbContext.SaveChanges(); update wird die gesamte Einheit. Wenn Sie sagen, dass EF explizit, welche Eigenschaften zu aktualisieren, nur sind diese Eigenschaften aktualisiert. dbSet.Attach(entity); dbContext.Entry(entity).Property(e => e.Title).IsModified = true; dbContext.SaveChanges(); update nur Titel. Mit deaktiviert die Validierung, das funktioniert gut.
  • Mit diesem Ansatz(explizit sagen, EF, welche Eigenschaften zu aktualisieren) auch kann ich update eine andere Eigenschaft (entity.IsActive = false), aber bisher habe ich nicht markieren Sie ihn als aktualisiert (e => e.IsActive).IsModified = true; es wird nicht aktualisiert. entity.State = EntityState.Modified markiert die gesamte Einheit als sofort aktualisiert.
  • Sie müssen nicht zum abrufen der Entität zu markieren als gelöscht, nur ein einfaches SQL-Anweisung mit dbSet.SqlCommand("UPDATE entity SET deleted = true where x = y"), das ist VIEL einfacher als der Weg, den Sie nehmen.
  • Ich denke du bist über-engineering eine Lösung. Es darf heute arbeiten, aber AFAIK ist dies keine unterstützte Möglichkeit, dies zu tun, was bedeutet, es kann brechen in einer zukünftigen version. Die Tatsache, dass Sie die Probleme sollten Ihnen sagen, dass es nicht, in der Tat, tatsächlich funktioniert. Stub-Entitäten werden unterstützt, aber Sie können nur der Schlüssel-Wert wird unterstützt. Wenn Sie mehr als nur die Schlüssel, es ist kein stub mehr.
  • BTW, stub-Einheiten sind nicht mehr die bevorzugte Methode, eher die FK Vereine sind. Und Sie sind leistungsfähiger.
  • Glaube nicht, über die raw-sql 🙂 vielen Dank für Eure Antworten, Jungs

InformationsquelleAutor Skog | 2012-10-13

3 Kommentare

  1. 22

    Wenn Sie partielle updates oder stub-Entitäten (beide Ansätze sind ziemlich gültig!) Sie können die Globale EF-Validierung, weil es nicht respektieren Ihre teilweise änderungen – es überprüft die gesamte Einheit. Mit Standard-Validierungs-Logik müssen Sie es deaktivieren, indem Sie den Aufruf erwähnt:

    dbContext.Configuration.ValidateOnSaveEnabled = false

    Validieren und jeder aktualisierte Eigentum getrennt. Dies sollte hoffentlich machen die Magie, aber ich versuchte es nicht, weil ich nicht verwenden, EF-Validierung an alle:

    foreach(var prop in propsToUpdate) {
        var errors = contextEntry.Property(prop).GetValidationErrors();
        if (erros.Count == 0) {
            contextEntry.Property(prop).IsModified = true;
        } else {
            ...
        }
    }

    Wenn Sie möchten, gehen Sie Schritt weiter, können Sie versuchen, überschreiben ValidateEntity in Ihrem Kontext und implementieren Validierung in der Weise, dass es überprüft die gesamte Einheit oder nur ausgewählte Eigenschaften, basierend auf den Zustand der Entität und IsModified Zustand der Eigenschaften, die Ihnen erlauben, mit EF-Validierung mit partiellen updates und stub-Entitäten.

    Validierung in EF ist IMHO falsch Konzept – es führt auch zusätzliche Logik in den data-access-layer, wo die Logik nicht gehört. Es basiert hauptsächlich auf der Idee, dass Sie arbeiten immer mit der ganzen Person oder sogar mit ganzen entity-Diagramm, wenn Sie legen Sie die erforderlichen Validierung Regeln, die auf der Navigations-Eigenschaften. Sobald Sie gegen diese Vorgehen, Sie werden immer finden, dass die einzigen festen set von Prüfregeln fest auf Ihre Entitäten nicht ausreichend.

    Eines der Dinge, die ich in meinem sehr langen Rückstau ist es zu untersuchen, wie die Validierung betrifft die Geschwindigkeit der SaveChanges Betrieb – früher habe ich meine eigenen validation-API in EF4 (vor EF4.1) basierend auf DataAnnotations und Ihre Validator Klasse und ich habe aufgehört, Sie Recht bald durch eine sehr schlechte Leistung.

    Workaround mittels native SQL hat die gleichen Auswirkungen wie die Verwendung von stub-Entitäten oder teilweise mit updates ausgeschaltet validation = Ihre Einheiten sind noch nicht validiert, aber neben deinen änderungen sind nicht Teil der gleichen Einheit arbeiten.

    • Selbst wenn Sie deaktivieren entity-Validierung ist nicht alles in Ordnung ist. Zum Beispiel habe ich Eigenschaften die einen unveränderten Wert, aber wenn ich nicht explizit den Wert dieser Requisiten alle implizit, default-Werte, z.B. 0 für integer. Das Ergebnis ist, dass es wirklich funktioniert, müssen Sie verwenden Sie ROH-Sql gibt es keine Problemumgehung oder Lösung für dieses Szenario durch EF 6.x
    • Wenn Sie haben unveränderten Wert sollten Sie diesen Wert gefüllt (also default-Wert wird nicht verwendet) oder verwenden Sie partielle updates und don ‚ T pass Sie in Aktualisierungen der Datenbank.
    • Option1: ich möchte nicht haben, dass mit dem Wert gefüllt, weil dann muss ich gehen, um die Datenbank wieder für jede Entität ich das update Option2: ich verstehe nicht, können Sie bitte ein wenig code? oder beschreiben mehr? Ich verwendet, teilweise auch mit update-Eigenschaft.IsModified = true; was fehlt hier?
    • Ich meinte nicht diejenigen markieren, die Eigenschaften geändert werden, wenn Sie nicht möchten, senden Sie diese Standard-Werte zurück zur Datenbank.
    • Aber ich glaube NICHT, markieren Sie die Eigenschaften, wie geändert. Ich habe gerade EINE Eigenschaft.IsModified = true nicht mehr.
    • Gut habe ich wohl falsch verstanden, dein problem. So werden die anderen Eigenschaften sind auf Standardwerte gesetzt, sondern nur in der Anwendung (nicht in der Datenbank), ist es richtig? Sind die Eigenschaften, die null-Werte zulässt? Wenn nicht, müssen Sie einen Wert haben, so müssen Sie entweder legen Sie den Wert explizit oder default-Wert verwendet.
    • Diese Eigenschaften sind alle NICHT NULL in der Datenbank. Die eine Eigenschaft, die ich zu ändern und zu aktualisieren, ist NULL. Die content-Eigenschaft ist text, aber nicht der text. Jetzt sagen Sie, ich habe, um den Wert explizit, aber das würde bedeuten, dass ich haben, um Daten aus der Datenbank, die ich nicht auf der Update-Vorgang. Das ist dumm von EF. Dann habe ich bessere stick mit meiner RAW-SQL-2-liner… vielen Dank für Ihre Zeit.
    • Ich habe ein Beispiel Filter, Validierung der Eigenschaften, die IsModified nur.
    • So schalten wir die Validierung auf den Kontext und lassen die Modelle ansehen zu geben. Das ist eine ziemlich bedeutende basteln mit EF!

  2. 18

    In Bezug auf Ladislav Antwort, ich habe dies nur Hinzugefügt, um die DbContext Klasse, und er entfernt nun alle die Eigenschaften, die nicht verändert werden.

    Ich weiß, das ist nicht ganz überspringen der Validierung für die Eigenschaften, sondern nur weglassen, aber EF überprüft, pro Person, die nicht Eigentum, und das umschreiben der gesamten Validierungsprozess von neuem, es war zu viel Aufwand für mich.

    protected override DbEntityValidationResult ValidateEntity(
      DbEntityEntry entityEntry,
      IDictionary<object, object> items)
    {
      var result = base.ValidateEntity(entityEntry, items);
      var falseErrors = result.ValidationErrors
        .Where(error =>
        {
          if (entityEntry.State != EntityState.Modified) return false;
          var member = entityEntry.Member(error.PropertyName);
          var property = member as DbPropertyEntry;
          if (property != null)
            return !property.IsModified;
          else
            return false;//not false err;
        });
    
      foreach (var error in falseErrors.ToArray())
        result.ValidationErrors.Remove(error);
      return result;
    }
    • danke!!! dies ist der eleganteste Weg, die ich gefunden, aber ich würde auch vorschlagen, um diesen Zustand if (entityEntry.State != EntityState.Modified) return false; innerhalb der Where, um sicher zu sein, entfernen Sie die falseErrors nur beim aktualisieren der Person, da sonst bei der addition der Validierung werden alle entfernt, nur weil die Flagge IsModified ist nicht festgelegt.
    • Added mich einfach ein if (entityEntry.State == EntityState.Modified) einwickeln der var falseErrors und die foreach um zu verhindern, dass das Verhalten Michael Denny beschrieben. Besser als es in der Linq-Anfrage.
    • Hey Shimmy, cheers für dieses snippet. Frage mich, ob Sie wissen, ob diese noch benötigt werden-EF6.1? Ich hatte gerade eine Prüfung, und EF6 war von 2013, aber es scheint wie etwas, das konnte ich da verbessert worden, auf der EF-Seite seit dem letzten April?
    • Danke, das half.
    • Ich fügte hinzu, dass. Ich glaube nicht, verwenden Sie diese nicht mehr, so ich weiß nicht, ob es funktionieren würde.
    • Sie können einfach Bearbeiten Sie diese post statt-Sie wissen…

  3. 2

    Dies ist ein remix von früheren @Shimmy Antwort und es ist eine version, die ich derzeit verwenden.

    Was ich Hinzugefügt habe, ist die Klausel (entityEntry.State != EntityState.Modified) return false; im Where:

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        var result = base.ValidateEntity(entityEntry, items);
    
        var falseErrors = result
            .ValidationErrors
            .Where(error =>
            {
                if (entityEntry.State != EntityState.Modified) return false;
                var member = entityEntry.Member(error.PropertyName);
                var property = member as DbPropertyEntry;
                if (property != null) return !property.IsModified;
                return false;
            });
    
        foreach (var error in falseErrors.ToArray())
        {
            result.ValidationErrors.Remove(error);
        }
    
        return result;
    }

Kostenlose Online-Tests