Casablanca: Validierung in MVC

Hallo Forum,

ich brauche heute wieder mal eure Unterstützung. Das Problem liegt unter MVC und zwar bei Validierung der Datenfelder. Ich habe als Beispiel dieses Eingabefeld:

  
<div>  
   <%: Html.Label("Verbindung")%>  
</div>  
<div class="editor-field">  
   <%: Html.TextBoxFor(model => model.Verbindung)%>  
   <%: Html.ValidationMessage("Verbindung", "*")%>  
</div>  

Ich validiere alle felder durch eine eigene Klasse "public IEnumerable<RuleViolation> getRuleViolations() { }" wie folgt:

  
public IEnumerable<RuleViolation> getRuleViolations()  
{  
   ...  
   if (String.IsNullOrEmpty(Verbindung))  
      yield return new RuleViolation("Verbindung required", "Verbindung");  
   ...  
}  

Das funktioniert alles auch sehr gut, aber nur bei den Feldern, die in MSSQL als varchar definiert sind. Bei allen anderen Feldern bekomme ich eine doppelte Validierungsmeldung, einmal das, was ihr oben sieht, nämlich "Verbindung required" und einmal dasselbe in Deutsch "Das Feld "Verbindung" ist erforderlich.", die ich gar nicht definiert habe. Ich weiß nicht, woher die zweite Meldung kommt. Kann man diese eventuell abschalten?

Beim Einfügen solcher Felder, werden diese auch bei if (ModelState.IsValid) {} als Invalid erkannt, was mir sehr merkwürdig vorkommt. Was noch seltsamer ist, behalten mansche Felder Ihre vorherigen Werte, obwohl ich bei denen den Inhalt gelöscht habe. Z.B bei "if (String.IsNullOrEmpty(Verbindung)" hat die Verbindung immer nocht einen Wert, obwohl ich auf dem Formular den Wert durch die Taste "Entf" gelöscht habe.

Danke im Voraus.
Gruß

  1. Tach!

    Das funktioniert alles auch sehr gut, aber nur bei den Feldern, die in MSSQL als varchar definiert sind. Bei allen anderen Feldern bekomme ich eine doppelte Validierungsmeldung, einmal das, was ihr oben sieht, nämlich "Verbindung required" und einmal dasselbe in Deutsch "Das Feld "Verbindung" ist erforderlich.", die ich gar nicht definiert habe. Ich weiß nicht, woher die zweite Meldung kommt. Kann man diese eventuell abschalten?

    Die Frage kann man erst beantworten, wenn man weiß, wo die Meldung herkommt. Ich tippe darauf, dass du in deinem Model bei dem entsprechenden Feld ein [Required]-Attribut oder etwas ähnliches stehen hast. Außerdem kann ich mir nicht vorstellen, dass die Regel für Verbindung auf andere Felder passt, denn die haben ja andere Namen. Die Verbindungsmeldung kann nur kommen, wenn ebenfalls die Verbindung leer oder null ist. Wenn du das in bestimmten Fällen nicht prüfen willst, musst du deine Logik entsprechend anpassen. Deine gezeigte Methode untersucht ja das Model als ganzes und nicht nur einzelne Felder.

    Was noch seltsamer ist, behalten mansche Felder Ihre vorherigen Werte, obwohl ich bei denen den Inhalt gelöscht habe. Z.B bei "if (String.IsNullOrEmpty(Verbindung)" hat die Verbindung immer nocht einen Wert, obwohl ich auf dem Formular den Wert durch die Taste "Entf" gelöscht habe.

    Dann kommt vielleicht irgendwoher ein Default-Wert. Da hilft nur Debuggen, wenn du nichts verdächtiges in deinem Model entdeckst. Die Controller-Action bekommt entweder ein Objekt der Model-Klasse übergeben oder die Inhalte in einer allgemeine Form (war es ein Dictionary? ich weiß es grad nicht). Diese Werte sind eventuell schon bearbeitet. Was wirklich kommt, kannst du irgendwo in den Tiefen des Request-Objekts finden. Wenn du das vergleichst, weißt du zumindest, ob der Wert schon vom Browser kommt oder von ASP.NET MVC hinzugefügt wird.

    dedlfix.

    1. Hi,

      in der Tabelle sind einige der Ferlser als Nullabel = NO definiert. Das überträgt sich auch eventuell auf das Model (Meinst du das?). Das ist aber auch für Varchar-Felder der Fall. So sollte auch das Problrm auftreten, was nicht der Fall ist.

      In der Klasse, wo ich das Verbindung-Fled steht, stehen auch andere Felder, für die auch andere Bedingungen gelten. Das erklärt aber nicht das seltsame Verhalten der Valisierung. Was ich auch eben nicht verstehe, wie bereits erwähnt, ist der Wert der Felder. Ich kann mir nicht vorsettel, dass ein Feld seinen Wert behält, obwohl im Feld nichts drin ist. Der Inhalt der Felder werden erst dann zum Server geschickt, wenn man den Save-Button betätigt hat und da gibt es in diesem Feld nicht drin.

      Ich habe das Ganze noch einmal überprüft. Wie es scheint wird das Objekt, das ich mir aus der Datenbank hole (Infos info = getInfo(id)), bei UpdateModel(info) nicht richtig upgedatet. Normalerweise soll das Objekt info durch UpdateModel(info); mit den aktuellen/neuen Inhalte des Formulars upgedated werden, dass man im nächsten Schritt in die Datenbank schreiben kann. Das ist aber seltsam.

      Gruß

      1. Tach!

        in der Tabelle sind einige der Ferlser als Nullabel = NO definiert. Das überträgt sich auch eventuell auf das Model (Meinst du das?).

        Ja. Non-Nullable-Felder können keine leeren Eingaben speichern. Bei Integer ist das klar, da muss man mindestens eine 0 eingeben. Ein leeres Eingabefeld würde zu null und das geht ja nicht ins DBMS einzutragen. Bei Stringfeldern ist das ähnlich, ein Leerstring wird zu null umgewandelt. Aus Datenbanksicht ist ein Leerstring zwar ein von NULL verschiedener Wert, die hätte also kein Problem. Aber das Entity Framework hat nicht so recht eine Chance, einen Null-Wert in einer Eingabe zu erkennen. Es gibt schließlich kein NULL in HTML. Und ein leeres Input-Feld ergibt in den POST/GET-Daten zumindest einen Eintrag mit Feldnamen und keinem Wert. So nimmt man dann den Leerstring und macht daraus ein null. Dieses kann nun nicht in der Datenbank abgelegt werden, weswegen ein Speicherversuch zu einer Exception führt. Ich lass dann dem Varchar-Feld sein NULLable und hab meine Ruhe. Wenn du nun unbedingt drauf angewiesen bist, im DBMS zwischen NULL und Leerstring zu unterscheiden, dann müsstest du dich selbst mal auf Lösungssuche begeben.

        Das Nullable=false im Integer-Feld jedenfalls sorgt dafür, dass sich das Model schon selbst beschwert, ohne dass du da eine Validierungsgeschichte hinzufügst. Wenn du den Meldungstext ändern willst, musst du dazu dem Feld im Model eine Data Annotation hinzufügen: ein Required-Attribut mit ErrorMessage-Parameter.

        In der Klasse, wo ich das Verbindung-Fled steht, stehen auch andere Felder, für die auch andere Bedingungen gelten. Das erklärt aber nicht das seltsame Verhalten der Valisierung. Was ich auch eben nicht verstehe, wie bereits erwähnt, ist der Wert der Felder. Ich kann mir nicht vorsettel, dass ein Feld seinen Wert behält, obwohl im Feld nichts drin ist.

        Definiere "behält". Wie hast du das beobachtet? Wenn beispielsweise das Speichern wegen der NULL-Geschichte misslingt und du die Exteption mundtot gemacht hast, bleibt der Wert im DBMS natürlich unverändert, ohne dass du dazu was siehst.

        Ich habe das Ganze noch einmal überprüft. Wie es scheint wird das Objekt, das ich mir aus der Datenbank hole (Infos info = getInfo(id)), bei UpdateModel(info) nicht richtig upgedatet.

        Das sollte sich in irgendeiner Form bemerkbar machen, entweder durch eine Exception oder durch einen Rückgabewert.

        dedlfix.

        1. Hallo,

          es gibt ja ein Feld in Datenbank namens "Verbindung" von Typ float. Dieses Feld hat z.B. den Wert 123, der aus der Datenbank als Objekt ausgelen wurde und jestzt steht im Feld drin. Jetzt lösche ich diesen Wert aus dem Feld und klicke auf den Save-Button. In der Action-Methode hole ich wie vorhin erwähnt ein Objekt mit den aktuellen Inhalten vom Datenbak, in der auch im Feld Verbindung noch 123 steht. Nach dem UpdateModel(info) versucht nun die Methode etwas leeres (ich weiß nicht ob das ein String ist oder etwas anderes) in das Objekt für "Verbindung" reinzuschreiben. Da dieser Vorgang scheitert, wird ein Exception geworfen, in der steht dass das Modell nicht aktualisiert werden konnte. Da dieser Vorgang gescheitert ist, behält das Feld Verbindung in dem erwähnten Objekt seinen ursprünglichen Wert (123) bei, weil es ja nicht aktualiseirt werden konnte (durch Haltepunkt deutlich sichbar). Daher kann ich auch in meiner ValidierungsKlasse nicht herausfinden, ob nun das Feld leer ist oder nicht.

          Nun bekomme ich allerdings die Deutschsprachige Meldung und nicht meine eigene. Kannst du bitte etwas näher beschreiben, wie "dazu dem Feld im Model eine Data Annotation hinzufügen: ein Required-Attribut mit ErrorMessage-Parameter." zu machen ist.

          Gruß

          1. Tach!

            es gibt ja ein Feld in Datenbank namens "Verbindung" von Typ float. Dieses Feld hat z.B. den Wert 123, der aus der Datenbank als Objekt ausgelen wurde und jestzt steht im Feld drin. Jetzt lösche ich diesen Wert aus dem Feld und klicke auf den Save-Button. In der Action-Methode hole ich wie vorhin erwähnt ein Objekt mit den aktuellen Inhalten vom Datenbak, in der auch im Feld Verbindung noch 123 steht. Nach dem UpdateModel(info) versucht nun die Methode etwas leeres (ich weiß nicht ob das ein String ist oder etwas anderes) in das Objekt für "Verbindung" reinzuschreiben. Da dieser Vorgang scheitert, wird ein Exception geworfen, in der steht dass das Modell nicht aktualisiert werden konnte. Da dieser Vorgang gescheitert ist, behält das Feld Verbindung in dem erwähnten Objekt seinen ursprünglichen Wert (123) bei, weil es ja nicht aktualiseirt werden konnte (durch Haltepunkt deutlich sichbar). Daher kann ich auch in meiner ValidierungsKlasse nicht herausfinden, ob nun das Feld leer ist oder nicht.

            Beim Validieren ist es auch schon zu spät. Du prüfst ja nur noch auf Gültigkeit und nimmst nicht mehr die Eingabedaten entgegen. Das ist bereits in einem früheren Schritt passiert (und auch nicht Aufgabe der Validierung). Nun hast du ein Problem, dass du die Eingabedaten nicht mehr untersuchen kannst (kannst du doch, aber dann über das Request-Objekt), weil sie bereits in dein strongly-typed Model-Objekt überführt worden sind. Ein Null oder Leerstring passt da nicht in ein int. Da müsstest du schon int? (oder ausgeschrieben Nullable<int>) nehmen. Das passt aber nicht auf dein DBMS-Model, also müsstest du dir ein separates Model mit int? nur für die Eingabedaten erstellen. Nach der Validierung musst du dann die Daten in das DBMS-Model-Objekt übernehmen.

            UpdateModel() sollte eigentlich gar nicht mehr gebraucht werden. Aktuelle ASP.NET-MVC-Versionen machen das anders, die übergeben gleich ein fertig gefülltes Objekt.

            Nun bekomme ich allerdings die Deutschsprachige Meldung und nicht meine eigene. Kannst du bitte etwas näher beschreiben, wie "dazu dem Feld im Model eine Data Annotation hinzufügen: ein Required-Attribut mit ErrorMessage-Parameter." zu machen ist.

            Das hängt davon ab, welchen ORM du verwendest. Generell hört das auf den Namen Data Annotations. Das sollte dir begegnet sein, wenn du ein ASP.NET-MVC-Tutorial durcharbeitest. Wenn du dein Model nach Code First erstellst, dann kannst du da die Attribute in den Propertys unterbringen. Bei Database First wird es schwieriger, weil da ein Script deinen Model-Klassen-Code erzeugt. Die Klasse ist zwar partial, und damit erweiterbar, aber das nützt bei Propertys nichts. Es lassen sich da keine Attribute hinzufügen. Der Ansatz ist da ein wenig anders über eine Hilfsklasse. Aber das findest du alles über die genannten Stichwörter.

            dedlfix.