Asp.net mvc ASP.Net MVC Html.HiddenFor的值错误

Asp.net mvc ASP.Net MVC Html.HiddenFor的值错误,asp.net-mvc,asp.net-mvc-3,hidden-fields,html.hiddenfor,Asp.net Mvc,Asp.net Mvc 3,Hidden Fields,Html.hiddenfor,我在我的项目中使用MVC3,我看到一个非常奇怪的行为 我试图为模型上的特定值创建一个隐藏字段,问题是由于某种原因,字段上设置的值与模型中的值不对应 e、 g 我有以下代码,作为测试: <%:Html.Hidden("Step2", Model.Step) %> <%:Html.HiddenFor(m => m.Step) %> m、 步骤)%%> 我认为两个隐藏字段的值相同。 我要做的是,在第一次显示视图时将值设置为1,然后在提交后将模型字段的值增加1 因此,

我在我的项目中使用MVC3,我看到一个非常奇怪的行为

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

e、 g

我有以下代码,作为测试:

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

m、 步骤)%%>
我认为两个隐藏字段的值相同。 我要做的是,在第一次显示视图时将值设置为1,然后在提交后将模型字段的值增加1

因此,第一次呈现页面时,两个控件的值均为1,但第二次呈现的值为:

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

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

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


谢谢你的帮助。

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

我在编写向导时遇到了相同的问题,该向导在每一步都会显示较大模型的不同部分。
“步骤1”中的数据和/或错误将与“步骤2”等混淆,直到我最终意识到ModelState是“罪魁祸首”

这是我的简单解决方案:

if(oldPageIndex!=newPageIndex)
{

ModelState.Clear();//此代码将不起作用

// 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 always(!)读取的是ModelState而不是模型本身。如果找不到“Step”键,它将生成该变量类型的默认值,在本例中为0

这里是解决方案。我为自己写的,但不介意分享,因为我看到很多人都在与这个淘气的隐藏的帮手斗争

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)
            };
        }
    }
}

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

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

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


不确定这是否有帮助,但只考虑…

我刚刚测试了其他的东西。如果我删除了隐藏的调用,只允许隐藏的调用,但是使用“步骤”名称,它也只呈现第一个值(1)。。同样发生在get中。我非常感谢Simon Ince关于这一点的博客文章。我从中得出的结论是确保您的工作流是正确的。因此,如果您接受了有效的视图模型并对其进行了处理,则重定向到确认操作,即使这也只是检索并显示了一个等效的模型。这意味着您有一个fresh ModelState。(链接自我今天写的一篇文章:)我真的很喜欢MVC3,但这一点真的很笨重。我希望他们能在MVC4中修复它。哇,这一次让我经历了相当长的时间。我基本上使用了第一个建议,但在返回之前只调用了ModelState.Clear()。这似乎很有效,有什么理由不使用Clear吗“.Remove”对我来说不起作用。但是ModelState.Clear()在返回控制器之前就起作用了。自定义编写隐藏也会起作用。这一切都是因为开发人员不希望在按“提交”时丢失其“表单值”“数据库无法正确保存。最佳解决方案:不要在同一页上以相同的名称命名不同的字段/id.FYI此恼人的行为被优雅地转移到ASP.NET Core,以防有人担心情况会好转
ModelState.Clear()
在类似情况下通过顺序POST请求解决了我的问题。感谢ModelState.Clear()提示Evan。这是我以前从未遇到过的异常情况。我有几个连续的ajax.beginform帖子,其中一个帖子保留了前一篇帖子中的值。调试黑洞。有人知道为什么会缓存它吗?这个解决方案没有完全工作。在ActionWell中的next post back属性为null后,这是我从生产中获得的代码工作正常。我不能告诉你为什么它对你不起作用,但是如果你看到页面上呈现的隐藏字段值正确,我看不出有明显的原因为什么它不会被恢复到模型的属性中。但是如果你在页面上看到错误的隐藏字段值-这是另一个故事,我很想知道在什么情况下会发生这种情况在同样的事情发生在我的作品之前:-)谢谢。
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)