Подтвердить что ты не робот

ASP.NET MVC и ViewState

Теперь я видел несколько таких вопросов, но это не совсем то, что я хочу спросить, поэтому для всех этих кричащих дубликатов я прошу прощения:).

Я едва коснулся ASP.NET MVC, но из того, что я понимаю, нет ViewState/ControlState... отлично. Итак, мой вопрос заключается в том, что является альтернативой сохранению контрольного состояния? Вернемся ли мы к старой школьной ASP, где мы могли бы имитировать то, что делает ASP.NET ViewState/ControlState, создавая скрытые входные данные формы с управляющим состоянием или с MVC, мы просто предполагаем, что AJAX всегда и сохраняем всю клиентскую сторону штата и делаем AJAX звонки для обновления?

В этом вопросе есть несколько ответов Поддержание просмотра в Asp.net mvc?, но не совсем то, что я ищу в ответ.

ОБНОВЛЕНИЕ: Спасибо за все ответы. Просто чтобы выяснить, что я не ищу и что я ищу:

Не ищу:

  • Решение сеанса
  • Решение для файлов cookie
  • Не хочет имитировать WebForms в MVC

Что я/искал:

  • Метод, который сохраняет состояние только при обратной передаче, если данные не восстанавливаются до элемента управления. Подумайте, что WebForms имеет сценарий привязки сетки к начальной загрузке страницы, т.е. Только при необходимости восстанавливает данные. Как я уже упоминал, я не пытаюсь имитировать WebForms, просто интересно, какие механизмы предлагает MVC.
4b9b3361

Ответ 1

Соглашение уже доступно без перескакивания через слишком много обручей. Хитрость заключается в подключении значений 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, который принимает формуCollection и сопоставляет ключи внутри коллекции с именами/значениями ID, которые у вас есть, с помощью Html-помощников. Например:

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

Обратите внимание, что текстовое поле и текстовое поле имеют идентификаторы Title и Body? Теперь обратите внимание, как я устанавливаю значения из объекта View Model? Поскольку вы прошли в FormCollection (и вы должны установить, что представление будет строго типизировано с помощью FormCollection), вы можете получить к нему доступ. Или, без строгого ввода, вы можете просто использовать ViewData [ "Title" ] (я думаю).

POOF Ваше волшебное ViewState. Эта концепция называется условной конфигурацией.

Теперь приведенный выше код находится в его самой простой, самой сырой форме с использованием FormCollection. Все становится интересным, когда вы начинаете использовать ViewModels вместо FormCollection. Вы можете приступить к добавлению собственной проверки ваших моделей /ViewModels и заставить контроллер автоматически вызывать ошибки пользовательской проверки. Это ответ на другой день.

Я бы предложил использовать PostFormViewModel вместо объекта Post, но для каждого из них. В любом случае, потребовав объект к методу действия, теперь вы получите метод 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 и т.д.).

Ответ 2

В шаблоне MVC предполагается, что представление нечеткое, просто показывает, что дает ему контроллер (очевидно, что мы часто оказываемся с некоторой логикой там, но предпосылка заключается в том, что он не должен быть) в результате, t ответственный за их состояние, он будет поступать от контроллера каждый раз.

Я не могу рекомендовать книгу Стивена Сандерсона Pro ASP.NET MVC от Apress достаточно для того, чтобы справиться с этим шаблоном и его реализацией.

Ответ 3

В веб-формах управляющие значения сохраняются в представлении, поэтому вы (теоретически) не нуждаетесь в повторной инициализации и при каждой обратной передаче. Значения (теоретически) поддерживаются каркасом.

В ASP.NET MVC, если вы следуете парадигме, вам не нужно поддерживать состояние элементов формы. Значения элементов формы доступны на почте, где ваш контроллер может действовать на них (проверка, обновления базы данных и т.д.). Для любых элементов формы, которые отображаются после обработки сообщения, вы (разработчик) отвечаете за их инициализацию - фреймворк автоматически не делает этого для вас.

Тем не менее, я прочитал о механизме под названием TempData, который позволяет вашему контроллеру передавать данные другому контроллеру после перенаправления. Это фактически переменная сеанса (или файл cookie, если вы его настраиваете как таковой), но он автоматически очищается после следующего запроса.

Ответ 4

  • скрытые поля, например:

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%>
      <%= Html.Hidden("SomeField", Model.SomeField)%>
      <%= Html.Hidden("AnotherField", Model.AnotherField)%>
    
  • настройка конкретной модели и отсутствие явных полей (дает скрытые поля). В приведенном ниже примере модель заполняется контроллером со значениями, полученными от последнего сообщения, поэтому это позволяет на странице не использовать параметр 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" />
                <% } %>
    
  • использовать методы расширения для создания полей, если вы просто хотите, чтобы поля были заполнены опубликованными значениями, когда u показывает неудачные сообщения проверки в представленной форме
  • на asp.net mvc 2 они ввели способ сохранить экземпляр в скрытом поле... закодировано + (я думаю) подписано
  • TempData, если все вышеперечисленное не делает этого (проходит через сеанс - очищается при следующем запросе)
  • как указано выше, при использовании ajax состояние уже находится в ранее загруженных полях на клиентском сайте. Если вам нужно сделать полный пост, обновите любое поле, которое вам может понадобиться с помощью js.

Вышеприведенные все различные независимые варианты для достижения этого, которые могут использоваться в разных сценариях. Есть больше вариантов, о которых я не упоминал: cookie, session, store stuff в db (например, для возобновляемого мастера с несколькими шагами), параметры, переданные в действие. Нет единого механизма, чтобы управлять ими всеми, и не должно быть.

Ответ 5

Ответ действительно зависит от типов элементов управления, для которых вы пытаетесь сохранить состояние. Для базовых элементов управления Html вам очень легко поддерживать состояние с вашими моделями, для этого вам нужно создать строго типизированный вид.

Итак, если бы у нас была модель User со свойствами: Username, FullName, Email, мы можем сделать следующее в представлении:

<%= 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>
<% } %>

Тогда у нас было бы два действия контроллера, которые отображают это представление, одно для get и другое для сообщения:

[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 и информация о сеансе.

Как говорили другие люди, я настоятельно рекомендую Steve Sandersan Pro ASP.NET MVC Framework для полного понимания работы с MVC Framework.

Ответ 6

Лучший способ сделать это, я думаю, - сериализовать вашу оригинальную модель на скрытое поле, затем десериализировать ее и обновить модель на столбе. Это несколько похоже на подход 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, который может преобразовать объект в строку.

Локальная среда (ограниченная сериализация объектов) используется в представлении в веб-формах. Он также предлагает шифрование данных сериализации.

здоровается...

Ответ 7

AJAX звонки - это то, что мы делаем. Если вы говорите о сетках в целом, посмотрите JQGrid и как они рекомендуют реализацию AJAX.