Вопрос

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

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

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

Обновить:Спасибо за все ответы на данный момент.Просто чтобы прояснить, чего я не ищу и что я ищу:

Не ищу:

  • Сессионное решение
  • Решение для использования файлов cookie
  • Не хотите имитировать веб-формы в MVC

То, что я есть / искал:

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

Решение

Конвенция уже доступна, не перепрыгивая через слишком много обручей.Хитрость заключается в том, чтобы связать значения текстового поля на основе модели, которую вы передаете в представление.

[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-помощники.Например:

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

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

ПУФ Ваше волшебное состояние просмотра.Эта концепция называется соглашением по сравнению с конфигурацией.

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

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

Много-много условностей исключено.Вот несколько книг, которые я настоятельно рекомендую, в которых эти паттерны рассматриваются гораздо более подробно:

И в таком порядке первый охватывает исходные элементы всего фреймворка MVC.Последнее охватывает передовые методы, не входящие в официальную версию Microsoft relm, с несколькими внешними инструментами, которые значительно облегчат вашу жизнь (Castle Windsor, Moq и т.д.).

Другие советы

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

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

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

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

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

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

Итак, если бы у нас была пользовательская модель со свойствами:Имя пользователя, полное имя, адрес электронной почты, мы можем сделать следующее в представлении:

<%= 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 и другое для 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 и информация о сеансе.

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

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

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

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

как вы можете видеть, в форму добавлено скрытое поле (Модель).Он содержит информацию о сериализации для исходного объекта.Когда форма публикуется, скрытое поле также публикуется (конечно), и содержимое десериализуется пользовательским modelbinder в исходный объект, который затем обновляется и сохраняется контроллером.

Обратите внимание, что объект, который вы сериализуете, должен быть оформлен атрибутом Serializable или должен иметь TypeConverter, который может преобразовать объект в строку.

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

приветствует...

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top