ASP.Net MVC Postback и модели

В основном это комментарий к комментарию в этом выпуске, но у меня недостаточно репутации для комментариев...

ASP.Net MVC Возвращает значение метки для вашего контроллера

Скажем, у меня есть простая модель:

public class SimpleClass
{
    public String Label { get; set; }
    public String FirstName { get; set; }
}

Метка изменяется на основе пользователя/клиента, поэтому она не может быть атрибутом DataAttribute. Если при возникновении проблем с обратной обработкой необходимо перерисовать всю страницу. В этом суть проблемы предыдущего поста. Принятое решение состоит в том, чтобы сделать это:

@Html.DisplayTextFor(model => model.Label)
@Html.HiddenFor(model => model.Label)
@Html.EditorFor(model => model.FirstName)

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

Это приводит меня к комментарию JP:

ASP.Net MVC Возвращает значение метки для вашего контроллера

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

default: SimpleClass { Label="TheLabel", FirstName="Rob"}
postedback: SimpleClass { Label="", FirstName="Steve" }
we want: SimpleClass { Label="TheLabel", "FirstName="Steve" }

Мой вопрос заключается в том, имеет ли MVC хороший способ узнать, какие поля были отправлены назад, чтобы он сливался правильно? Нам нужно было бы только объединить поля обратной связи, а не пустые свойства.

Или лучше просто просто удалить всю ретрансляцию и не отправить форму? Это позволяет избежать проблем с перезагрузкой модели при отправке.

Update

Чтобы дать кредит Пабло, я принял его решение. Чтобы увидеть мой простой пример его решения, отметьте комментарий Роберта Харви в нижеприведенных ответах:

ASP.Net MVC Postback и модели

Ответ 1

Основная проблема здесь заключается в попытке установить концепции PostBack WebForms в MVC. Нет такой вещи, как ретрансляция с сохранением состояния, когда вещи просто автоматически сохраняют свое состояние.

У вас есть только ViewModels, привязанные к представлению, и ViewModels, которые отображаются в представлении на контроллере. Они даже не обязательно должны быть одного типа. Смысл, контроллер должен получать только данные, которые пользователь действительно может изменить, а не большие объекты со многими свойствами, которые были частью исходного ViewModel, но доступны только для чтения.

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

И да, иногда это означает, что вам нужно перезагрузить исходные данные в контроллере и синхронизировать с новыми данными, которые вы опубликовали, что не обязательно плохо. Если вы привязываете данные только для чтения к представлению, которое пользователь не может вручную редактировать, вы не должны действительно доверять тому, что данные возвращаются в сообщение после этого. Просто потому, что ваш html может попытаться сделать его доступным только для чтения, не означает, что я не могу манипулировать сообщением и в конечном счете изменять ваши данные только для чтения, не зная.

Я просто прочитал второй вопрос, который вы упомянули, и из его взглядов его основная проблема заключалась в том, что он снова пытался использовать один и тот же ViewModel, поэтому все данные отсутствовали, а модель недействительна. Решение этого действительно простое, ТОЛЬКО опубликуйте, что вам нужно, как новый тип ViewModel, и пусть контроллер позаботится об остальном.

Ответ 2

[Перемещено с OP]

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

Модели:

    public class SimpleClass : SimpleClassPostBack
    {
        public String Label { get; set; }

        public SimpleClass()
        {
            // simulate default loading
            Label = "My Label";
            FirstName = "Rob";
        }

    }

    // contains only editable by the user fields
    public class SimpleClassPostBack
    {
        public String FirstName { get; set; }
    }

Действия контроллера:

    [HttpGet]
    public ActionResult SimpleClassExample3()
    {
        SimpleClass simpleClass = new SimpleClass();
        return View(simpleClass);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SimpleClassExample3(SimpleClassPostBack postBackSimpleClass)
    {
        Boolean errorOccurred = true;

        if (!errorOccurred)
        {
            // do whatever success action is necessary
        }

        // redraw the page, an error occurred

        // reload the original model
        SimpleClass simpleClass = new SimpleClass();

        // move the posted back data into the model
        // can use fancy reflection to automate this
        simpleClass.FirstName = postBackSimpleClass.FirstName;

        // bind the view
        return View(simpleClass);
    }

Вид:

@model SimpleClass

@{
    ViewBag.Title = "Simple Class Example3";
}

<h2>Simple Class Example3</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <label for="FirstName">@Html.DisplayFor(m => m.Label)</label>
    @Html.EditorFor(m => m.FirstName)

    <br/>

    <button>Submit</button>

}

Ответ 3

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

Используйте скрытые поля для идентификации объектов базы данных. Таким образом, ваш SimpleClass должен, вероятно, иметь какой-то Id, который вы будете использовать в скрытом вводе. Используйте EditorFor для FirstName. Теперь, когда форма отправлена, используйте отправленный Id, чтобы найти правильный SimpleClass из базы данных и изменить его свойство FirstName со значением, опубликованным. Свойство Label будет null, что нормально, так как вам не нужно его сохранять. Теперь, если есть проблема в сообщении, и вы хотите отправить тот же вид назад, как и было, вам нужно повторно заполнить Label так же, как вы, когда пользователь впервые появился в представлении. Значения свойств Id и FirstName будут автоматически отправлены обратно в представление с состоянием модели.

Вкратце:

  • Только отправьте данные, которые необходимы для определения чего-либо и того, что пользователь могут редактировать в этом представлении.
  • Не доверяйте клиенту отправлять вам что-либо действительное. Пользователь может изменить значения ярлыков скрытых полей на что-либо.