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

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

我想念什么?* For HTML帮助程序是否以某种方式缓存值?如果是这样,如何禁用此缓存?

谢谢你的帮助。


我只是测试了其他东西。如果删除HiddenFor调用并仅允许Hidden调用,但使用“ Step”名称,它也仅呈现第一个值(1)。
willvv 2011年

1
也会发生
Oren A

Answers:


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
我非常感谢Simon Ince的博客文章。我从中得出的结论是确保您的工作流程正确。因此,如果您已经接受了有效的视图模型并对其进行了某些操作,则将其重定向到确认操作,即使这只是简单地检索并显示等效模型也是如此。这意味着您将拥有一个新的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
“。删除”对我不起作用。但是ModelState.Clear()在Controller返回之前就做了。自定义编写您的“隐藏”也可以很好地工作。这一切都是因为开发人员不想按“提交”并且数据库未正确保存而丢失其“表单值”。最佳解决方案:不要在同一页面上用相同的名称/ ID命名不同的字段。
Dexter 2014年

1
仅供参考,如果有人担心情况会变得更好,此烦人的行为将被慷慨地转移到ASP.NET Core
John Hargrove

19

在编写向导时会遇到相同的问题,该向导在每个步骤都显示较大模型的不同部分。
来自“第1步”的数据和/或错误将与“第2步”混合,以此类推,直到我最终意识到ModelState被“怪罪”。

这是我的简单解决方案:

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

return View(model[newPageIndex]);

10
ModelState.Clear()在类似情况下通过顺序POST请求解决了我的问题。
埃文·穆拉斯基

感谢ModelState.Clear()技巧Evan。这是我从未遇到过的异常。我有几个连续的ajax.beginform帖子,其中一个是保留先前帖子的值。调试黑洞。有人知道为什么要缓存吗?
罗布(Rob

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)

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


此解决方案无法完全正常工作。在下一次发布后属性在操作中为空
user576510 2014年

好吧,这是生产环境中可以正常工作的代码。我无法说出为什么它对您不起作用,但是如果您在页面上看到具有正确值的隐藏字段,则看不到没有明显的原因,为什么它不会还原到模型的属性中。但是,如果您在页面上看到错误的隐藏字段值-那是另一个故事,我很想知道在什么情况下会在我的产品上发生同样的情况之前发生这种情况:-)谢谢。
Ruslan Georgievskiy 2014年

0

我在相同的情况下实在太挣扎了,我在两次调用之间使用相同的模型状态,并且在后端更改模型属性时也是如此。不过,对我而言,这无关紧要,如果我使用textboxfor或hiddenfor。

我只是通过使用页面脚本将模型值存储为js变量来绕过这种情况,因为一开始我需要为此目的使用hiddenfield。

不确定是否有帮助,但请考虑一下。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.