none
asp.net mvc 3 の Html.DisplayFor と Html.HiddenFor で値が異なってしまう現象 RRS feed

  • 質問

  • 開発環境:Windows7、VS2010 Pro SP1、.NET Framework4、C# で ASP.NET MVC3 の開発をしております。

    以下の様にPOSTデータをModelにバインドした後、特定のプロパティを書き換えた際、View(Razor)で Html.DisplayFor と Html.HiddenFor すると値が異なってレンダリングされてしまうという状況にはまってしまい苦慮しております。具体的には Html.DisplayFor は書き換え後の値、Html.HiddenFor は元の(POSTされてきた)値のままという現象です。考えられる原因等をご教授頂けると幸いです。何卒よろしくお願い申し上げます。

    ■Actionの例
    [HttpPost]
    public ActionResult Test(MyModel pModel)
    {
      pModel.MyProperty1 = "xxx";
      Return View(pModel);
    }

    ■Viewの例
    @model MyModel
    @Html.HiddenFor(model => model.MyProperty1) ←"xxx"とならない
    <p>@Html.DisplayFor(model => model.MyProperty1)</p> ←"xxx"となる

    2012年10月4日 10:15

回答

  • > pModel.MyProperty1 = "xxx";

    の前に、以下のコードを入れたらどうなりますか?

    if (ModelState.IsValid)
    {
        ModelState.Clear();
    }
    pModel = new <名前空間>.MyModel();

    ハズレだったら失礼しました。

    2012年10月4日 13:15
  • 小野さん>

    > Model復元時にエラーになっている場合、

    エラーにはなっていないようですが・・・

    > HiddenForとDisplayForでは参照する値が異なるってことですかね?

    自分もよく分かってないので、ググって調べてみました。

    以下のページによると、value が POST されるか否かが結果の違いによ
    るそうです。答えているのが Microsoft の .NET Team の人のようです
    ので、たぶん、間違いないと思います。

    A Bug? EditorFor and DisplayFor don't display samevalue -
    EditorFor out of date
    http://forums.asp.net/p/1527149/3687407.aspx

    上記は EditorFor と DispalyFor の話ですが、POST 要求をかけた場合
    は、EditorFor には、model の値ではなく、ポストされた値が表示され
    るとのことです。

    なぜポストされた値を使うかといえば、例えば、EditorFor の入力には
    整数を要求しているのに、ユーザーが間違えて "dog" と入力してポスト
    した場合、エラーメッセージで「"dog" という入力が間違っています」
    と表示するとともに、EditorFor には "dog" という表示を残したいとい
    う理由だそうです。

    その "dog" という値は ModelState ディクショナリ(model ではない)
    に格納されていて、HtmlHelper はまず ModelState ディクショナリを調
    べて、そこに値があればそれを表示するということのようです。

    ModelState ディクショナリを Clear すれば model から値を取ってくる
    ので、期待した結果になるということのようです。

    HiddenFor も、その value は POST されるので、結果は EditorFor と
    同じになる(POST された value が表示される)のだと思います。

    なお、DisplayFor には、ポストされた値などというものはないので、
    model の値が使われるとのことです。

    このあたりのもう少し詳しい説明が以下のページにあります。

    ASP.NET MVC’s Html Helpers Render the Wrong Value!
    http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx

    2012年10月5日 13:03

すべての返信

  • > pModel.MyProperty1 = "xxx";

    の前に、以下のコードを入れたらどうなりますか?

    if (ModelState.IsValid)
    {
        ModelState.Clear();
    }
    pModel = new <名前空間>.MyModel();

    ハズレだったら失礼しました。

    2012年10月4日 13:15
  • バッチリでした!

    ちなみにプロパティを書き換える処理より前に ModelState.Clear() を実行するだけで PModel は new しなくても問題は解決しました。ご参考まで。

    大変、助かりました。とても勉強になり本当にありがとうございました。

    2012年10月5日 0:24
  • 問題は解決された、ということになっていますが。

    ModelState.Clear() を実行する、ということはPOSTで受け取ったデータはすべて破棄してると思うのですが、それでシステム的に問題はないのですか?
    そもそもPOSTということは入力された値を受け取ることが前提だと思うのですが。。。

    それは別として。

    To:SurferOnWww さん

    Model復元時にエラーになっている場合、HiddenForとDisplayForでは参照する値が異なるってことですかね?
    このあたりご存知でしたら教えていただきたく。


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    2012年10月5日 1:42
  • ModelState.Clear()  を実行した後ですが Action 引数でバインドした変数(上記例ですと pModel )の各プロパティはクリアされませんでした。よって書き換えたいプロパティのみを書き換え、以降の処理は問題なく結果を得られたという状況です。

    ■Actionの例
    [HttpPost]
    public ActionResult Test(MyModel pModel)
    {
      if (ModelState.IsValid)
        ModelState.Clear();
      else
      {
        return View("Error"); /* エラー処理 */
      }

      pModel.MyProperty1 = "xxx"; ←他のプロパティはクリアされておらずPOSTされた値がバインドされたまま
      Return View(pModel);
    }

    ちなみに HiddenFor と DisplayFor では参照先が違うのか?という部分、問題が解決する前にデバックで追っかける限りでは同じ値がセットされてるのですが、ブラウザに送信されたHTML結果が何故か異なるという状況で???となり原因が分からず困っていた次第です。

    2012年10月5日 6:07
  • 気になったのでざっくりソース眺めてみました。

    #ご存知かと思いますが、ASP.NET MVCはソースが公開されています。

    まず、DisplayForとHiddenForですが、HiddenForのほうはInput系のヘルパーだという点が大きい違いになっているようです。
    このため、DisplayForはModelで受け取ったデータを直接表示しますが、HiddenForのほうはまずModelStateのほうをチェックし、そちらに情報があればその情報を表示する、ということのようです。

    Modelのバインディングでエラーが発生している(IsValidがtrue)ときにViewにそのModelを渡すと、エラーの表示とともにPOSTで受け取った値を表示できるわけですが、このあたりの仕組みが関連してるようですね。

    Modelにエラーが発生していなければこういう現象は発生していなかったはずなので、一度なにがエラーになっているのか確認しておいたほうがいいかもしれません。
    まぁ、エラーが必ずしも悪い?わけではなく、仕様の関係でここの処理だと必ずModeはエラーになる、ということはあり得ます。
    そういう場合はModelState.Clearを実行することで問題ないと思います。

    ModelStateとPostで受け取ったデータの関連は、まだ調べてないです(w
    ModelStateにはエラーになったもののデータだけ入ってるんだっけかな???


    あおい情報システム株式会社 小野修司(どっとねっとふぁん)

    2012年10月5日 8:18
  • InoueComputerService さん>

    > ちなみにプロパティを書き換える処理より前に ModelState.Clear()
    > を実行するだけで PModel は new しなくても問題は解決しました。

    そうでした。すでに model のインスタンスは存在するので新たに作る
    必要はなかったですね。失礼しました。

    2012年10月5日 13:02
  • 小野さん>

    > Model復元時にエラーになっている場合、

    エラーにはなっていないようですが・・・

    > HiddenForとDisplayForでは参照する値が異なるってことですかね?

    自分もよく分かってないので、ググって調べてみました。

    以下のページによると、value が POST されるか否かが結果の違いによ
    るそうです。答えているのが Microsoft の .NET Team の人のようです
    ので、たぶん、間違いないと思います。

    A Bug? EditorFor and DisplayFor don't display samevalue -
    EditorFor out of date
    http://forums.asp.net/p/1527149/3687407.aspx

    上記は EditorFor と DispalyFor の話ですが、POST 要求をかけた場合
    は、EditorFor には、model の値ではなく、ポストされた値が表示され
    るとのことです。

    なぜポストされた値を使うかといえば、例えば、EditorFor の入力には
    整数を要求しているのに、ユーザーが間違えて "dog" と入力してポスト
    した場合、エラーメッセージで「"dog" という入力が間違っています」
    と表示するとともに、EditorFor には "dog" という表示を残したいとい
    う理由だそうです。

    その "dog" という値は ModelState ディクショナリ(model ではない)
    に格納されていて、HtmlHelper はまず ModelState ディクショナリを調
    べて、そこに値があればそれを表示するということのようです。

    ModelState ディクショナリを Clear すれば model から値を取ってくる
    ので、期待した結果になるということのようです。

    HiddenFor も、その value は POST されるので、結果は EditorFor と
    同じになる(POST された value が表示される)のだと思います。

    なお、DisplayFor には、ポストされた値などというものはないので、
    model の値が使われるとのことです。

    このあたりのもう少し詳しい説明が以下のページにあります。

    ASP.NET MVC’s Html Helpers Render the Wrong Value!
    http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx

    2012年10月5日 13:03
  • SurferOnWww 様 小野 様

    いろいろとありがとうございました。
    SurferOnWwwさんのご指摘通りModelStateエラーにはなっておりませんのでエラーの有無ではないですね。SurferOnWwwさんがググって調べて頂いた情報で原因は明白になったかなと思います。問題は解決したのですがモヤモヤしてて、これですっきりしました。本当にありがとうございました。

    2012年10月5日 15:06