ASP.Net MVC Html.HiddenFor mit falschem Wert

132

Ich verwende MVC 3 in meinem Projekt und sehe ein sehr seltsames Verhalten.

Ich versuche, ein verstecktes Feld für einen bestimmten Wert in meinem Modell zu erstellen. Das Problem besteht darin, dass der auf dem Feld festgelegte Wert aus irgendeinem Grund nicht dem Wert im Modell entspricht.

z.B

Ich habe diesen Code nur als Test:

<%:Html.Hidden("Step2", Model.Step) %>
<%:Html.HiddenFor(m => m.Step) %>

Ich würde denken, dass beide versteckten Felder den gleichen Wert haben würden. Ich setze den Wert beim ersten Anzeigen der Ansicht auf 1 und erhöhe dann nach der Übermittlung den Wert des Felds Modell um 1.

Wenn ich die Seite zum ersten Mal rendere, haben beide Steuerelemente den Wert 1, aber beim zweiten Mal sind die gerenderten Werte folgende:

<input id="Step2" name="Step2" type="hidden" value="2" />
<input id="Step" name="Step" type="hidden" value="1" />

Wie Sie sehen können, ist der erste Wert korrekt, aber der zweite Wert scheint der gleiche zu sein wie beim ersten Anzeigen der Ansicht.

Was vermisse ich? Zwischenspeichern die * For Html-Helfer die Werte auf irgendeine Weise? Wenn ja, wie kann ich dieses Caching deaktivieren?

Danke für Ihre Hilfe.

willvv
quelle
Ich habe gerade etwas anderes getestet. Wenn ich den HiddenFor-Aufruf entferne und nur den Hidden-Aufruf lasse, aber den Namen "Step" verwende, wird auch nur der erste Wert (1) gerendert.
Willvv
1
passiert auch in get
Oren A

Antworten:

191

Das ist normal und so funktionieren HTML-Helfer. Sie verwenden zuerst den Wert der POST-Anforderung und danach den Wert im Modell. Dies bedeutet, dass selbst wenn Sie den Wert des Modells in Ihrer Controller-Aktion ändern, wenn dieselbe Variable in der POST-Anforderung vorhanden ist, Ihre Änderung ignoriert wird und der POST-Wert verwendet wird.

Eine mögliche Problemumgehung besteht darin, diesen Wert in der Controller-Aktion, die versucht, den Wert zu ändern, aus dem Modellstatus zu entfernen:

// remove the Step variable from the model state 
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

Eine andere Möglichkeit besteht darin, einen benutzerdefinierten HTML-Helfer zu schreiben, der immer den Wert des Modells verwendet und POST-Werte ignoriert.

Und noch eine Möglichkeit:

<input type="hidden" name="Step" value="<%: Model.Step %>" />
Darin Dimitrov
quelle
5
Ich habe den Blog-Beitrag von Simon Ince darüber sehr geschätzt. Die Schlussfolgerung, die ich daraus ziehe, ist sicherzustellen, dass Ihr Workflow korrekt ist. Wenn Sie also ein gültiges Ansichtsmodell akzeptiert und etwas damit getan haben, leiten Sie zu einer Bestätigungsaktion um, auch wenn dies auch einfach ein gleichwertiges Modell abruft und anzeigt. Dies bedeutet, dass Sie einen neuen ModelState haben. blogs.msdn.com/b/simonince/archive/2010/05/05/… (verlinkt von einem Beitrag, den ich heute dazu geschrieben habe: oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html )
Lisa
2
Ich mag MVC3 wirklich, aber dieses bisschen ist wirklich klobig. Ich hoffe, sie beheben es in MVC4.
KennyZ
5
Wow, dieser hat mich eine ganze Weile gehen lassen. Ich habe im Grunde den ersten Vorschlag verwendet, aber vor der Rückkehr nur ModelState.Clear () aufgerufen. Dies scheint großartig zu funktionieren. Gibt es einen Grund, Clear nicht zu verwenden?
Jason
1
Das ".Remove" hat bei mir nicht funktioniert. ModelState.Clear () hat dies jedoch direkt vor der Rückkehr in Controller getan. Das benutzerdefinierte Schreiben Ihres Hidden würde auch gut funktionieren. Dies alles geschieht, weil Entwickler ihre "Formularwerte" nicht verlieren möchten, wenn sie auf "Senden" klicken und die Datenbank nicht korrekt speichert. Beste Lösung: Benennen Sie nicht verschiedene Felder auf derselben Seite mit demselben Namen / derselben ID.
Dexter
1
Zu Ihrer Information, dieses nervige Verhalten wurde freundlicherweise auf ASP.NET Core übertragen, falls jemand befürchtete, dass die Dinge besser werden
John Hargrove
19

Beim Schreiben eines Assistenten, der bei jedem Schritt verschiedene Teile eines größeren Modells anzeigt, ist dasselbe Problem aufgetreten.
Daten und / oder Fehler aus "Schritt 1" würden mit "Schritt 2" usw. verwechselt, bis mir schließlich klar wurde, dass ModelState "schuld" war.

Das war meine einfache Lösung:

if (oldPageIndex != newPageIndex)
{
    ModelState.Clear(); // <-- solution
}

return View(model[newPageIndex]);
Peter B.
quelle
10
ModelState.Clear()löste mein Problem mit sequentiellen POST-Anfragen in einer ähnlichen Situation.
Evan Mulawski
Vielen Dank für den ModelState.Clear () -Tipp Evan. Dies war eine Anomalie, auf die ich noch nie gestoßen bin. Ich hatte mehrere aufeinanderfolgende ajax.beginform-Posts und einer von ihnen behielt die Werte eines vorherigen Posts bei. Schwarzes Loch debuggen. Weiß jemand, warum dies zwischengespeichert wird?
Rob
1

Dieser Code funktioniert nicht

// remove the Step variable from the model state
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

... weil HiddenFor immer (!) aus ModelState liest, nicht das Modell selbst. Wenn der "Schritt" -Schlüssel nicht gefunden wird, wird der Standardwert für diesen Variablentyp erstellt, der in diesem Fall 0 ist

Hier ist die Lösung. Ich habe es für mich selbst geschrieben, aber es macht mir nichts aus, es zu teilen, weil ich sehe, dass viele Leute mit diesem ungezogenen HiddenFor-Helfer zu kämpfen haben.

public static class CustomExtensions
{
    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    private static void ReplacePropertyState<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        string text = ExpressionHelper.GetExpressionText(expression);
        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text);
        ModelStateDictionary modelState = htmlHelper.ViewContext.ViewData.ModelState;
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        if (modelState.ContainsKey(fullName))
        {                
            ValueProviderResult currentValue = modelState[fullName].Value;
            modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture);
        }
        else
        {
            modelState[fullName] = new ModelState
            {
                Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), CultureInfo.CurrentUICulture)
            };
        }
    }
}

Dann verwenden Sie es einfach wie gewohnt aus Ihrer Sicht:

@Html.HiddenFor2(m => m.Id)

Erwähnenswert ist, dass es auch mit Sammlungen funktioniert.

Ruslan Georgievskiy
quelle
Diese Lösung hat nicht vollständig funktioniert. Nach dem nächsten Beitrag zurück Eigenschaft ist null in Aktion
user576510
Nun, dies ist der Code aus der Produktion, in dem er gut funktioniert. Ich kann nicht sagen, warum es bei Ihnen nicht funktioniert, aber wenn Sie das versteckte Feld mit dem korrekten Wert auf der Seite sehen, sehe ich keinen offensichtlichen Grund, warum es nicht in der Eigenschaft des Modells wiederhergestellt wird. Wenn Sie jedoch auf der Seite einen falschen Wert für versteckte Felder sehen - das ist eine andere Geschichte, würde ich sehr gerne wissen, unter welchen Umständen dies geschieht, bevor dasselbe bei meiner Produktion passiert :-) Vielen Dank.
Ruslan Georgievskiy
0

Ich habe zu viele Probleme mit der gleichen Situation, in der ich zwischen Aufrufen denselben Modellstatus verwende und wenn ich eine Modelleigenschaft im Backend ändere. Es ist mir jedoch egal, ob ich textboxfor oder hiddenfor verwende.

Ich umgehe die Situation einfach, indem ich Seitenskripte verwende, um den Modellwert als js-Variable zu speichern, da ich zu Beginn das versteckte Feld für diesen Zweck benötige.

Ich bin mir nicht sicher, ob dies hilft, aber denke nur darüber nach.

abdulkadir pekeroglu
quelle