17

今、私はこのような質問をいくつか見てきましたが、それは私が聞きたいこととは正確には異なります.

ASP.NET MVC にはほとんど触れていませんが、私が理解していることから、ViewState/ControlState はありません... 結構です。私の質問は、コントロールの状態を保持する代わりに何ができるでしょうか? コントロールの状態または MVC を使用して非表示のフォーム入力を作成することによって ASP.NET ViewState/ControlState が行うことをシミュレートする古い学校の ASP に戻りますか? AJAX を常に想定し、すべての状態をクライアント側に保持して AJAX を作成します更新するために呼び出しますか?

この質問にはいくつかの答えがあります。、しかし、私が答えで探しているものとは正確には異なります。

更新: これまでのすべての回答に感謝します。私が探していないものと探しているものを明確にするために:

探していない:

  • セッションソリューション
  • クッキーソリューション
  • MVC で WebForms を模倣しようとしていない

私が探していた/探していたもの:

  • データがコントロールに再バインドされていない場合にのみ、ポストバック時に状態を保持するメソッド。WebForms は、最初のページの読み込み時にのみグリッドをバインドするシナリオ、つまり必要な場合にのみデータを再バインドするシナリオを考えてみてください。前述したように、私は WebForms を真似ようとしているわけではなく、MVC が提供するメカニズムを知りたいだけです。
4

7 に答える 7

16

コンベンションは、あまりにも多くのフープを飛び越えることなく、すでに利用可能です。秘訣は、ビューに渡すモデルに基づいて TextBox 値を接続することです。

[AcceptVerbs(HttpVerbs.Get)]   
public ActionResult CreatePost()
{
  return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(FormCollection formCollection)
{
  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(formCollection);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(formCollection);
  }
}

次に発生するのは、ViewEngine が formCollection を受け取り、Html ヘルパーを使用して、コレクション内のキーをビュー内の ID 名/値と照合することです。例えば:

<div id="content">

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: <%= Html.TextBox("Title", Model["Title"], 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model["Body"]) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

textbox と textarea の ID が Title と Body であることに注意してください。ここで、View の Model オブジェクトから値を設定する方法に注目してください。FormCollection を渡したので (そして FormCollection で厳密に型指定されるようにビューを設定する必要があります)、アクセスできるようになりました。または、厳密に入力せずに、単に ViewData["Title"] を使用できます (私は思います)。

POOFあなたの魔法の ViewState。この概念は、設定より規約と呼ばれます。

上記のコードは、FormCollection を使用した最も単純で生の形式です。FormCollection の代わりに ViewModel を使い始めると、興味深いことが起こります。モデル/ビューモデルの独自の検証の追加を開始し、コントローラーにカスタム検証エラーを自動的にバブルさせることができます。しかし、それは別の日の答えです。

Post オブジェクトの代わりに PostFormViewModel を使用することをお勧めします。いずれにせよ、アクション メソッドでオブジェクトを要求することにより、呼び出し可能な IsValid() メソッドを取得できます。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(Post post)
{

  // errors should already be in the collection here
  if (false == ModelState.IsValid())
    return View(post);

  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(post);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(post);
  }
}

また、厳密に型指定されたビューを微調整する必要があります。

<div id="content">

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: <%= Html.TextBox("Title", Model.Title, 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model.Body) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

さらに一歩進んで、コントローラーで設定した ModelState から直接、ビューにエラーを表示することもできます。

<div id="content">

  <%= Html.ValidationSummary() %>

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: 
    <%= Html.TextBox("Title", Model.Title, 50) %>
    <%= Html.ValidationMessage("Title") %><br />

  Enter the Post Body: 
    <%= Html.TextArea("Body", Model.Body) %>
    <%= Html.ValidationMessage("Body") %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

このアプローチの興味深い点は、ビューで検証の概要や個々の検証メッセージを設定していないことに気付くことです。私は DDD の概念を実践するのが好きです。つまり、私の検証メッセージ (および要約) は私のドメインで制御され、コレクションの形で渡されます。次に、コレクションをループして (エラーが存在する場合)、それらを現在の ModelState.AddErrors コレクションに追加します。あとはView(post)を返すと自動です。

たくさんのコンベンションが出ています。これらのパターンをより詳細にカバーする、私が強くお勧めする数冊の本は次のとおりです。

この順序で、最初の部分は MVC フレームワーク全体の生の基本事項をカバーしています。後者は、Microsoft の公式レルム以外の高度なテクニックをカバーしており、作業をより簡単にするいくつかの外部ツール (Castle Windsor、Moq など) を使用しています。

于 2009-11-05T03:32:58.967 に答える
4

View は MVC パターンではダムであると想定されており、コントローラーが提供するものを表示するだけです (明らかに、いくつかのロジックで終わることがよくありますが、そうではないことが前提です)。その結果、コントロールは責任を負いません。それらの状態は、毎回コントローラーから取得されます。

このパターンとその実装を理解するには、Steven Sanderson の著書 Pro ASP.NET MVC by Apress をお勧めできません。

于 2009-09-24T18:57:57.520 に答える
3

答えは、状態を維持しようとしているコントロールの種類によって異なります。基本的な Html コントロールの場合、モデルで状態を維持するのは非常に簡単です。これを行うには、厳密に型指定されたビューを作成する必要があります。

したがって、Username、FullName、Email というプロパティを持つ User モデルがある場合、ビューで次の操作を実行できます。

<%= Html.ValidationSummary() %>

<% using (Html.BeginForm()) { %>
  <fieldset>
    <legend>User details</legend>
    <%= Html.AntiForgeryToken() %>

    <p>
      <label for="Username">Username:</label>
      <%= Html.Textbox("Username", Model.Username, "*") %>
    </p>
    <p>
      <label for="FullName">FullName:</label>
      <%= Html.Textbox("FullName", Model.FullName, "*") %>
    </p>
    <p>
      <label for="Email">Email:</label>
      <%= Html.Textbox("Email", Model.Email, "*") %>
    </p>
    <p>
       <input type+"submit" value="Save user" />
    </p>
  </fieldset>
<% } %>

次に、このビューを表示する 2 つのコントローラー アクションを作成します。1 つは get 用で、もう 1 つは post 用です。

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult User()
{
  return View(new User())
}

[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult User([Bind(Include = "Username,FullName,Email")]User user)
{
   if (!ModelState.IsValid()) return View(user);

   try
   {
     user.save()
     // return the view again or redirect the user to another page
   }
   catch(Exception e)
   {
     ViewData["Message"] = e.Message;
     return View(user)
   }
}

これはあなたが探しているものですか?または、リクエスト間でフォームに表示されていないモデルの状態を維持したいですか?

覚えておくべき重要なことは、コードはリクエストの期間中サーバー上で実行され、終了するということです。リクエスト間で渡すことができる唯一の情報は、基本的な html フォーム データ、url パラメーター、およびセッション情報です。

他の人が言及しているように、MVC フレームワークの操作を完全に理解するには、Steve Sandersan の Pro ASP.NET MVC Framework を強くお勧めします。

于 2009-11-05T17:59:14.300 に答える
3
  • 次のような隠しフィールド:

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%>
      <%= Html.Hidden("SomeField", Model.SomeField)%>
      <%= Html.Hidden("AnotherField", Model.AnotherField)%>
    
  • 特定のモデルを設定し、明示的なフィールドを持たない (隠しフィールドを提供する)。以下の例では、最後の投稿から受け取った値がコントローラーによってモデルに入力されるため、ステータスに基づいてフィルター処理できるページで no js オプションが有効になります。

    Some Filter: <% using( Html.BeginForm<SomeController>(
            c => c.SomeAction(model.SomeField, model.AnotherField, model.YetAnotherField, null, model.SomeOtherField)
            )) { %>
                <%= Html.DropDownList("status", Model.StatusSelectList)%>
                <input type="submit" value="Filter" class="button" />
                <% } %>
    
  • 送信されたフォームに失敗した検証メッセージが表示されているときにフィールドに投稿された値を入力したい場合は、拡張メソッドを使用してフィールドを作成します。
  • asp.net mvc 2で、インスタンスを隠しフィールドに保存する方法が導入されました...エンコードされた+(と思います)署名されました
  • 上記のすべてがそれを行わない場合のTempData(セッションを通過-次のリクエストで消去)
  • 前述のように、ajax を使用する場合、状態はクライアント サイトの以前に読み込まれたフィールドに既に存在します。完全な投稿を行う必要がある場合は、js で必要なフィールドを更新してください。

上記はすべて、さまざまなシナリオで使用できる、それを達成するためのさまざまな独立したオプションです。言及しなかったオプションが他にもあります。つまり、Cookie、セッション、db への保存 (再開可能なマルチステップ ウィザードなど)、アクションに渡されるパラメーターです。それらすべてを支配する単一のメカニズムはありませんし、存在すべきではありません。

于 2009-11-05T17:38:35.193 に答える
3

Web フォームでは、コントロール値はビューステートに保持されるため、(理論的には) ポストバックごとに再初期化する必要はありません。値は (やはり理論的には) フレームワークによって維持されます。

ASP.NET MVC では、パラダイムに従えば、フォーム要素の状態を維持する必要はありません。フォーム要素の値は、コントローラーがそれらに基づいて動作できる投稿で利用できます (検証、データベースの更新など)。投稿が処理された後に表示されるフォーム要素については、あなた (開発者) がそれらを初期化する責任があります。フレームワークは自動的にそれを行いません。

そうは言っても、リダイレクト後にコントローラーが別のコントローラーにデータを渡すことを可能にする TempData と呼ばれるメカニズムについて読んだことがあります。これは実際にはセッション変数 (またはそのように設定した場合は Cookie) ですが、次のリクエスト後に自動的にクリーンアップされます。

于 2009-11-05T04:00:01.707 に答える
2

これを行う最善の方法は、元のモデルを隠しフィールドにシリアル化し、それを逆シリアル化し、投稿時にモデルを更新することだと思います。これは、viewstate アプローチにいくぶん似ていますが、自分で実装する必要があるだけです。私はこれを使用します:

まず、物事を簡単にするいくつかの方法が必要です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using LuvDaSun.Extensions;
using System.Web.UI;

namespace LuvDaSun.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        static LosFormatter _losFormatter = new LosFormatter();
        public static string Serialize(this HtmlHelper helper, object objectInstance)
        {
            var sb = new StringBuilder();
            using (var writer = new System.IO.StringWriter(sb))
            {
                _losFormatter.Serialize(writer, objectInstance);
            }
            return sb.ToString();
        }


    }

    [AttributeUsage(AttributeTargets.Parameter)]
    public class DeserializeAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new DeserializeModelBinder();
        }
    }

    public class DeserializeModelBinder : IModelBinder
    {
        static LosFormatter _losFormatter = new LosFormatter();

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType.IsArray)
            {
                var type = bindingContext.ModelType.GetElementType();
                var serializedObjects = (string[])bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string[]));
                var deserializedObjects = Array.CreateInstance(bindingContext.ModelType.GetElementType(), serializedObjects.Length);

                for (var index = 0; index < serializedObjects.Length; index++)
                {
                    var serializedObject = serializedObjects[index];
                    var deserializedObject = _losFormatter.Deserialize(serializedObject);

                    deserializedObjects.SetValue(deserializedObject, index);
                }

                return deserializedObjects;
            }
            else
            {
                var serializedObject = (string)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string));
                var deserializedObject = _losFormatter.Deserialize(serializedObject);

                return deserializedObject;
            }
        }
    }

}

次に、コントローラーに次のようなものがあります(製品を更新するため)

    public ActionResult Update(string productKey)
    {
        var model = _shopping.RetrieveProduct(productKey);

        return View(model);
    }
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Update([Deserialize]Shopping.IProduct _model, FormCollection collection)
    {
        UpdateModel(model);

        model.Save();

        return RedirectAfterPost();
    }

そして、シリアル化されたオブジェクトを次の形式で保持する隠しフィールドが必要です。

    <% 
        using (Html.BeginRouteForm("Product", FormMethod.Post, new { id = UniqueID, }))
        {
    %>
<%= Html.Hidden("Model", Html.Serialize(Model)) %>
    <h1>
        Product bewerken</h1>
    <p>
        <label for="<%=UniqueID %>_Name">
            Naam:</label>
        <input id="<%=UniqueID %>_Name" name="Name" type="text" value="<%= Html.AttributeEncode(Model.Name) %>"
            class="required" />
        <br />
    </p>
    <p>
        Omschrijving:<br />
        <textarea id="<%= UniqueID %>_Description" name="Description" cols="40" rows="8"><%= Html.Encode(Model.Description) %></textarea>
        <br />
    </p>
    <p>
        <label for="<%=UniqueID %>_Price">
            Prijs:</label>
        <input id="<%= UniqueID %>_Price" name="Price" type="text" value="<%= Model.Price.ToString("0.00") %>"
            class="required" />
        <br />
    </p>
    <ul class="Commands">
        <li><a href="" class="ClosePopup">Annuleren</a></li>
        <li>
            <input type="submit" value="Opslaan" /></li>
    </ul>
    <% 
        } 
    %>

    <script type="text/javascript">

        jQuery('#<%= UniqueID %>').validate();

    </script>

ご覧のとおり、隠しフィールド (モデル) がフォームに追加されます。元のオブジェクトのシリアル化情報が含まれています。フォームが投稿されると、隠しフィールドも投稿され (もちろん)、コンテンツはカスタム モデルバインダーによって元のオブジェクトに逆シリアル化され、コントローラーによって更新および保存されます。

シリアル化するオブジェクトは、Serializable 属性で装飾する必要があるか、オブジェクトを文字列に変換できる TypeConverter が必要であることに注意してください。

LosFormatter (Limited Object Serialization) は、Web フォームのビューステートで使用されます。また、シリアル化データの暗号化も提供します。

よろしく...

于 2010-02-26T17:23:07.640 に答える
0

AJAX 呼び出しは私たちが行っていることです。グリッド全般について話している場合は、JQGridと、彼らがどのように AJAX 実装を推奨しているかを調べてください。

于 2009-11-04T22:13:34.817 に答える