ASP.Net MVC Html.HiddenFor с неправильным значением

132

Я использую MVC 3 в своем проекте и наблюдаю очень странное поведение.

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

например

У меня есть этот код, как тест:

<%:Html.Hidden("Step2", Model.Step) %>
<%:Html.HiddenFor(m => m.Step) %>

Я бы подумал, что оба скрытых поля будут иметь одинаковое значение. Что я делаю, так это устанавливаю значение 1 при первом отображении представления, а затем после отправки я увеличиваю значение поля Модель на 1.

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

<input id="Step2" name="Step2" type="hidden" value="2" />
<input id="Step" name="Step" type="hidden" value="1" />

Как видите, первое значение правильное, но второе значение кажется таким же, как и при первом отображении представления.

Чего мне не хватает? Кэшируют ли значения как-то вспомогательные программы * For Html? Если да, то как мне отключить это кеширование ?.

Спасибо за вашу помощь.

willvv
источник
Я только что протестировал другое. Если я удалю вызов HiddenFor и разрешу только вызов Hidden, но с использованием имени «Шаг», он также отобразит только первое значение (1).
willvv
1
тоже происходит в get
Oren A

Ответы:

191

Это нормально, и так работают помощники HTML. Сначала они используют значение запроса POST, а затем значение в модели. Это означает, что даже если вы измените значение модели в действии вашего контроллера, если в запросе POST есть такая же переменная, ваше изменение будет проигнорировано и будет использоваться значение POSTed.

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

// remove the Step variable from the model state 
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

Другая возможность - написать собственный помощник HTML, который всегда будет использовать значение модели и игнорировать значения POST.

И еще одна возможность:

<input type="hidden" name="Step" value="<%: Model.Step %>" />
Дарин димитров
источник
5
Мне очень понравилось сообщение Саймона Инса об этом в блоге. Вывод, который я делаю из этого, - убедиться, что ваш рабочий процесс правильный. Поэтому, если вы приняли допустимую модель представления и что-то с ней сделали, перенаправьте на действие подтверждения, даже если оно также просто возвращается и отображает эквивалентную модель. Это означает, что у вас свежий ModelState. blogs.msdn.com/b/simonince/archive/2010/05/05/… (ссылка на сообщение, которое я написал сегодня: oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html )
Лиза
2
Мне очень нравится MVC3, но этот бит действительно неуклюжий. Надеюсь, они исправят это в MVC4.
KennyZ
5
Вау, это заставляло меня работать довольно долго. Я в основном использовал первое предложение, но перед возвращением просто вызвал ModelState.Clear (). Кажется, это отлично работает, есть ли причина не использовать Clear?
Джейсон
1
".Remove" у меня не сработал. Но ModelState.Clear () сделал это прямо перед возвратом в Controller. Индивидуальное написание вашего Hidden также подойдет. Все это происходит потому, что разработчики не хотят терять свои «значения формы», если они нажимают «отправить», а БД не сохраняет правильно. Лучшее решение: не называйте разные поля на одной странице одним и тем же именем / идентификатором.
Dexter
1
К вашему сведению, это раздражающее поведение было любезно перенесено в ASP.NET Core на случай, если кто-то будет беспокоиться, что все станет лучше
Джон Харгроув
19

Я столкнулся с той же проблемой при написании мастера, который на каждом этапе показывает разные части большой модели.
Данные и / или ошибки из «Шага 1» смешивались с «Шагом 2» и т.д., пока я, наконец, не понял, что «виноват» ModelState.

Это было мое простое решение:

if (oldPageIndex != newPageIndex)
{
    ModelState.Clear(); // <-- solution
}

return View(model[newPageIndex]);
Питер Б
источник
10
ModelState.Clear()решил мою проблему с последовательными запросами POST в аналогичной ситуации.
Эван Мулавски
Спасибо за подсказку ModelState.Clear (), Эван. Это была аномалия, с которой я никогда раньше не сталкивался. У меня было несколько последовательных сообщений ajax.beginform, и один из них сохранял значения из предыдущего сообщения. Отладка черной дыры. Кто-нибудь знает, почему это кешируется?
Роб
1

Этот код не будет работать

// remove the Step variable from the model state
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

... потому что HiddenFor всегда (!) читает из ModelState, а не саму модель. И если он не найдет клавишу «Шаг», он создаст значение по умолчанию для этого типа переменной, которое в данном случае будет равно 0.

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

public static class CustomExtensions
{
    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    private static void ReplacePropertyState<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        string text = ExpressionHelper.GetExpressionText(expression);
        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text);
        ModelStateDictionary modelState = htmlHelper.ViewContext.ViewData.ModelState;
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        if (modelState.ContainsKey(fullName))
        {                
            ValueProviderResult currentValue = modelState[fullName].Value;
            modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture);
        }
        else
        {
            modelState[fullName] = new ModelState
            {
                Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), CultureInfo.CurrentUICulture)
            };
        }
    }
}

Затем вы просто используете его как обычно изнутри:

@Html.HiddenFor2(m => m.Id)

Стоит отметить, что он работает и с коллекциями.

Руслан Георгиевский
источник
это решение не полностью работало. После следующего поста свойство в действии имеет значение null
user576510
Ну, это код из производства, где он отлично работает. Я не могу сказать, почему это не работает для вас, но если вы видите скрытое поле с правильным значением, отображаемым на странице, я не вижу очевидной причины, по которой оно не было бы восстановлено в свойстве модели. Если вы видите неправильное значение скрытого поля на странице - это уже другая история, мне было бы очень интересно узнать, при каких обстоятельствах это произойдет, прежде чем то же самое произойдет в моем продукте :-) Спасибо.
Руслан Георгиевский
0

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

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

Не уверен, что это поможет, но просто подумайте ...

Абдулкадир Пекероглу
источник