136

我在我的项目中使用 MVC 3,我看到了一个非常奇怪的行为。

我正在尝试为我的模型上的特定值创建一个隐藏字段,问题是由于某种原因,该字段上设置的值与模型中的值不对应。

例如

我有这个代码,就像一个测试:

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

我认为这两个隐藏字段将具有相同的值。我所做的是,第一次显示 View 时将值设置为 1,然后在提交后将 Model 字段的值增加 1。

所以,我第一次呈现页面时,两个控件的值都是 1,但第二次呈现的值是:

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

如您所见,第一个值是正确的,但是第二个值似乎与我第一次显示视图时相同。

我错过了什么?*For Html 助手是否以某种方式缓存值?如果是这样,我该如何禁用此缓存?

谢谢你的帮助。

4

4 回答 4

197

这很正常,这就是 HTML 助手的工作方式。他们首先使用 POST 请求的值,然后使用模型中的值。这意味着即使您在控制器操作中修改模型的值,如果 POST 请求中有相同的变量,您的修改也将被忽略,并且将使用 POST 的值。

一种可能的解决方法是在尝试修改值的控制器操作中从模型状态中删除此值:

// 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 %>" />
于 2011-01-17T07:29:35.333 回答
22

在编写一个在每一步都显示较大模型的不同部分的向导时,我遇到了同样的问题。
“第 1 步”中的数据和/或错误会与“第 2 步”等混淆,直到我最终意识到 ModelState 应该“责备”。

这是我的简单解决方案:

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

return View(model[newPageIndex]);
于 2013-08-16T14:53:47.420 回答
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 读取而不是模型本身。如果它没有找到“Step”键,它将为该变量类型生成默认值,在这种情况下为 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)

值得一提的是,它也适用于集合。

于 2014-10-10T10:08:13.413 回答
0

我在同样的情况下也很挣扎,我在调用之间使用相同的模型状态,以及当我在后端更改模型属性时。不过,如果我使用 textboxfor 或 hiddenfor,对我来说并不重要。

我只是通过使用页面脚本将模型值存储为 js 变量来绕过这种情况,因为我一开始就需要隐藏字段。

不知道这是否有帮助,但只是考虑..

于 2018-04-06T12:38:47.867 回答