hiddenFor helper是从action参数中提取id,而不是viewModel

时间:2012-06-25 14:13:39

标签: asp.net-mvc-3 html-helper html.hiddenfor

我偶然发现了ASP.Net MVC的一个非常奇怪的情况,将“id”作为动作参数和“id”隐藏表单元素传递。我有一个具有“id”参数的动作。此id值表示项目。我有一个控制器操作,我正在创建一个数据输入表单,用于将员工分配给传入的项目(我创建一个下拉列表,管理员选择一个员工分配给传递的项目)。通过将员工分配给项目,我创建了一个ProjectEmployee记录(项目ID,员工ID和表示组合的ID,该组合是数据库中的标识列)。标识列(名为“id”)也是隐藏的表单元素。这是必要的,因为我需要能够在以后编辑项目/员工分配。

无论如何,在为项目创建新的员工分配时,项目ID(传递给操作的“id”)将应用于“id”隐藏的表单元素。

我正在通过117行动。 117是projectId。它被设置为“id”隐藏字段,该字段应该为0,因为0表示新项目/员工的分配。

查看模型

id  - unique id that represents the Project/Employee combination
projectId - the "Id" being passed to action
EmployeeId - what the admin is selecting from drop down
rate
startdate
endDate
...

数据输入表单

@Html.HiddenFor(m => m.Id) 
@Html.HiddenFor(m => m.ProjectId)
...
Id: @Model.Id <br />
ProjectId: @Model.ProjectId<br />

因此,@ Html.HiddenFor(m =&gt; m.Id)呈现隐藏的表单元素,其值为117. @ Model.Id将0呈现给UI。单步执行代码,我可以在视觉上看到Id属性的值为0.为什么HiddenFor将它的线交叉并拉出117的值?

这个bug已经进入生产阶段,所以我手上的数据搞得一团糟,因为不是在数据库表中创建新记录,我实际上是在更新现有记录,因为“Id”属性是错误地从0(表示新记录)设置为117(这是projectId),因此我正在更新不同的记录。

2 个答案:

答案 0 :(得分:6)

  

为什么HiddenFor会将它的电线交叉并拉出117的值?

这是设计的。所有HTML帮助程序(如TextBoxFor和HiddenFor)在绑定时首先使用ModelState的值,然后使用模型的值。我假设您的控制器操作如下所示:

public ActionResult Foo(int id)
{
    SomeModel model = ...
    // At this stage model.Id = 0
    return View(model);
}

问题是默认模型绑定器使用帮助器使用的键id将值添加到ModelState中,并忽略模型属性值。这不是一个错误。这是HTML帮助程序的设计方式。当你不知道它时,它在开始时有点混乱,但是一旦你习惯了这种行为,你就不会再次陷入陷阱。

解决问题的一种可能方法是从ModelState中删除此值:

public ActionResult Foo(int id)
{
    ModelState.Remove("id");
    SomeModel model = ...
    // At this stage model.Id = 0
    return View(model);
}

或者将模型中的Id属性重命名为其他内容以避免冲突。

答案 1 :(得分:1)

作为更改字段名称的替代方法,您可以在评估要在value属性中放置的内容时创建一个不使用路由值的新辅助方法。

我为极其常见的id案例创建了这个助手:

public static MvcHtmlString HiddenIdFor<TModel, TValue>(this HtmlHelper<TModel> html,
        Expression<Func<TModel, TValue>> expression)
    {
        var value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model.ToString();
        var builder = new TagBuilder("input");
        builder.MergeAttribute("type", "hidden");
        builder.MergeAttribute("name", ExpressionHelper.GetExpressionText(expression));
        builder.MergeAttribute("value", value);
        return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));

}

然后你可以在模板中使用它:

@Html.HiddenIdFor(m => m.Id) 
@Html.HiddenIdFor(m => m.ProjectId)