ASP.Net MVC Html.HiddenFor 值错误

Posted

技术标签:

【中文标题】ASP.Net MVC Html.HiddenFor 值错误【英文标题】:ASP.Net MVC Html.HiddenFor with wrong value 【发布时间】:2011-06-10 06:34:48 【问题描述】:

我在我的项目中使用 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" />

如您所见,第一个值是正确的,但第二个值似乎和我第一次显示 View 时一样。

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

感谢您的帮助。

【问题讨论】:

我刚刚测试了其他东西。如果我删除 HiddenFor 调用并只让 Hidden 调用,但使用“Step”名称,它也只呈现第一个值 (1)。 也发生在 get 中 【参考方案1】:

这很正常,这就是 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 %>" />

【讨论】:

我非常感谢 Simon Ince 的博客文章。我从中得出的结论是确保您的工作流程是正确的。因此,如果您接受了有效的视图模型并对其进行了某些操作,则重定向到确认操作,即使这也只是简单地检索并显示等效模型。这意味着你有一个新的 ModelState。 blogs.msdn.com/b/simonince/archive/2010/05/05/…(链接自我今天写的一篇文章:oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html) 我真的很喜欢 MVC3,但这一点真的很笨重。我希望他们在 MVC4 中修复它。 哇,这个让我走了很长一段时间。我基本上使用了第一个建议,但只是在返回之前调用了 ModelState.Clear() 。这似乎很好用,有什么理由不使用 Clear 吗? “.Remove”对我不起作用。但是 ModelState.Clear() 在控制器返回之前就做了。自定义编写您的 Hidden 也可以很好地工作。这一切都是因为开发人员不想在按下“提交”并且数据库没有正确保存时丢失他们的“表单值”。最佳解决方案:不要将同一页面上的不同字段命名为相同的名称/ID。 仅供参考,这种烦人的行为被慷慨地转移到 ASP.NET Core 中,以防有人担心事情会好转【参考方案2】:

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

这是我的简单解决方案:

if (oldPageIndex != newPageIndex)

    ModelState.Clear(); // <-- solution


return View(model[newPageIndex]);

【讨论】:

ModelState.Clear() 解决了我在类似情况下使用顺序 POST 请求的问题。 感谢 Evan 的 ModelState.Clear() 提示。这是我以前从未遇到过的异常情况。我有几个连续的 ajax.beginform 帖子,其中一个是保留以前帖子中的值。调试黑洞。有人知道为什么会被缓存吗?【参考方案3】:

此代码不起作用

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

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

【讨论】:

这个解决方案没有完全奏效。在下一次回发之后,Action 中的属性为空 嗯,这是来自生产环境的代码,它可以正常工作。我不知道为什么它对您不起作用,但是如果您看到页面上呈现的隐藏字段具有正确的值,我看不出它不会恢复到模型属性中的明显原因。如果您在页面上看到错误的隐藏字段值 - 那是另一回事,我很想知道在我的作品发生同样情况之前会发生这种情况:-) 谢谢。【参考方案4】:

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

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

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

【讨论】:

以上是关于ASP.Net MVC Html.HiddenFor 值错误的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net mvc和asp.net有啥区别?

Asp.net mvc和asp.net有啥区别?

七天学会ASP.NET MVC ——ASP.NET MVC 数据传递

ASP.NET MVC

ASP.NET MVC 5、ASP.NET Core MVC 5 有啥区别?

ASP.NET MVC 和 Angularjs 与 ASP.NET MVC 和 Reactjs