hiddenFor 帮助程序正在从操作参数中提取 id,而不是从视图模型中提取 id



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

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

我正在通过 117 进入行动。 117 是项目标识。 它被设置为"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 => m.Id) 呈现值为 117 的隐藏表单元素。 @Model.Id 将 0 呈现给 UI。 单步执行代码,我可以直观地看到 Id 属性的值为 0。 为什么 HiddenFor 会让它的电线交叉并拉动 117 的值?

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

为什么 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 帮助程序的设计方式。一开始你不知道的时候有点混乱,但是一旦你习惯了这种行为,你就不会第二次落入陷阱。

解决此问题的一种方法是从模型状态中删除此值:

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

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

作为更改字段名称的替代方法,您可以创建一个新的帮助程序方法,该方法在计算要在 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)

相关内容

  • 没有找到相关文章