Net MVC Html.HiddenFor 值错误

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

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

例如:。

我有这个代码,作为一个测试:

<%: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 助手是否以某种方式缓存值?如果是这样,我如何禁用这个缓存?.

谢谢你的帮助。

36945 次浏览

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

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

这是我的简单解决方案:

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


return View(model[newPageIndex]);

这个代码不起作用

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

... 因为隐藏永远(!)从 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)
};
}
}
}

然后你就像往常一样从你自己的角度来看:

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

值得一提的是,它也适用于收藏品。

我太纠结于同样的情况,我认为,我使用相同的模型状态之间的调用,当我改变后端的模型属性。不过,对我来说无所谓,如果我使用文本框或隐藏。

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

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