間違った値のASP.Net MVC Html.HiddenFor


132

プロジェクトでMVC 3を使用していますが、非常に奇妙な動作が見られます。

モデルの特定の値の非表示フィールドを作成しようとしていますが、問題は、何らかの理由でフィールドに設定された値がモデルの値に対応していないことです。

例えば

テストとして、私はこのコードを持っています:

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

両方の隠しフィールドは同じ値になると思います。まず、ビューを表示するときに値を1に設定し、送信後に[モデル]フィールドの値を1増やします。

したがって、最初にページをレンダリングするとき、両方のコントロールの値は1ですが、2回目にレンダリングされる値は次のとおりです。

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

ご覧のとおり、最初の値は正しいですが、2番目の値は、最初にビューを表示したときと同じように見えます。

何が欠けていますか?* For Htmlヘルパーは何らかの方法で値をキャッシュしていますか?もしそうなら、どうすればこのキャッシングを無効にできますか?

ご協力いただきありがとうございます。


私は何か他のものをテストしました。HiddenFor呼び出しを削除してHidden呼び出しのみを許可し、 "Step"名を使用すると、最初の値(1)のみが表示されます。
willvv

1
getでも発生します
Oren A

回答:


191

これは正常なことであり、HTMLヘルパーが機能する方法です。最初にPOSTリクエストの値を使用し、その後モデルの値を使用します。つまり、コントローラーアクションでモデルの値を変更しても、POSTリクエストに同じ変数がある場合、変更は無視され、POSTされた値が使用されます。

考えられる回避策の1つは、値を変更しようとしているコントローラーアクションのモデル状態からこの値を削除することです。

// 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;

別の可能性は、常にモデルの値を使用し、POST値を無視するカスタムHTMLヘルパーを作成することです。

そしてさらに別の可能性:

<input type="hidden" name="Step" value="<%: Model.Step %>" />

5
これについてはサイモンインスのブログ投稿を本当に感謝しています。私の結論は、ワークフローが正しいことを確認することです。したがって、有効なビューモデルを受け入れ、それを使用して何かを行った場合は、確認アクションにリダイレクトします。これも、同等のモデルを単に取得して表示する場合でも同様です。これは、新しいModelStateがあることを意味します。 blogs.msdn.com/b/simonince/archive/2010/05/05/… (今日私がこれについて書いた投稿からリンクされています:oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html
リサ

2
私はMVC3が本当に好きですが、このビットは本当に不格好です。MVC4で修正してくれるといいですね。
KennyZ 2012

5
うわー、これはかなり長い間私に行きました。基本的に最初の提案を使用しましたが、戻る前にModelState.Clear()を呼び出しました。これはうまく機能しているようですが、クリアを使用しない理由はありますか?
Jason

1
「.Remove」が機能しませんでした。しかし、ModelState.Clear()は、コントローラーに戻る直前に行いました。Hiddenのカスタム作成もうまくいきます。これはすべて、開発者が「送信」を押してもDBが正しく保存されない場合に「フォーム値」を失いたくないために発生します。最善の解決策:同じページの異なるフィールドに同じ名前/ IDを付けないでください。
デクスター

1
ちなみに、この厄介な動作は、状況が良くなるのではないかと心配された場合に備えて、ASP.NET Coreに優雅に引き継がれました
John Hargrove

19

大きなモデルのさまざまな部分をすべてのステップで表示するウィザードを作成するときに、同じ問題が発生しました。
「ステップ1」のデータやエラーは、「ステップ2」などと混同され、ModelStateが「原因」であることが最終的にわかりました。

これは私の簡単な解決策でした:

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

return View(model[newPageIndex]);

10
ModelState.Clear()同様の状況での順次POSTリクエストに関する私の問題を解決しました。
Evan Mulawski、2014年

ModelState.Clear()のヒントEvanをありがとう。これは、私が今まで遭遇したことのない異常でした。いくつかのajax.beginformの連続した投稿があり、そのうちの1つは以前の投稿の値を保持していました。ブラックホールのデバッグ。なぜこれがキャッシュされるのか誰でも知っていますか?
Rob

1

このコードは機能しません

// 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;

... HiddenForは常に(!)モデル自体ではなくModelStateから読み取るためです。そして、「ステップ」キーが見つからない場合、その変数タイプのデフォルトが生成されます。この場合は0になります。

これが解決策です。私は自分のためにそれを書きましたが、それを共有することを気にしないでください。多くの人々がこのいたずらなHiddenForヘルパーと格闘しているのを見ているからです。

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)
            };
        }
    }
}

次に、ビュー内から通常どおり使用します。

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

コレクションでも機能することは注目に値します。


このソリューションは完全には機能しません。次のポストバックプロパティは、アクションのnull後
user576510

まあ、これは正常に動作する本番のコードです。なぜそれが機能しないのかはわかりませんが、ページにレンダリングされた正しい値の非表示フィールドが表示された場合、モデルのプロパティに復元されない明確な理由はわかりません。ただし、ページに隠しフィールドの値が間違っている場合-これは別の話ですが、私の状況で同じことが発生する前に、これがどのような状況で発生するかを知りたいと思います:-)ありがとう。
Ruslan Georgievskiy 2014年

0

呼び出し間で同じモデルの状態を使用する場合や、バックエンドでモデルプロパティを変更する場合と同じ状況に苦しんでいます。ただし、textboxforまたはhiddenforを使用する場合、私には関係ありません。

ページスクリプトを使用してモデル値をjs変数として格納することで、状況を回避するだけです。最初にその目的のためにhiddenfieldが必要だからです。

これが役立つかどうかはわかりませんが、検討してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.