Razor 不会在 HiddenFor 中呈现隐藏的准确 PK
Razor will not render hidden accurate PK in HiddenFor
运行 在我的 ASP MVC 4 站点中遇到了一个奇怪的问题。如果用户打开 create
表单并尝试将代理添加到我们的数据库,我们自然会首先对字段进行 运行 验证。如果出现错误,我们将代理保存为未完成状态,并将用户重定向回创建页面。
用户尝试重新保存代理时出现错误。在第一个 post-back 中,代理被保存到数据库中并生成一个 PK。然而,在第二个 post-back 中,PK 被发送到服务器,其值为 0
而不是刚刚自动生成的值。
我在 Create
视图上添加了一个 HiddenFor
,但是每次呈现的值都是 0
。
我还逐步检查了代码,以确保在调用 .Save
后生成 PK,在调用 return View
时仍然存在,并确保 Model.ID
属性 在呈现视图时包含相同的值。
无论如何,如果我右键单击页面并查看源代码,隐藏字段呈现如下:
<input data-val="false" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="0" />
型号
public partial class AgentTransmission
{
public int ID { get; set; }
.
.
.
}
查看
@model MonetModelFromDb.Models.AgentTransmission
@{
ViewBag.Title = "Create new Agent";
}
@Html.HiddenFor(model => model.CreatedDate, new { data_val = "false" })
@Html.HiddenFor(model => model.CreatedOperator, new { data_val = "false" })
@Html.HiddenFor(model => model.ReferenceNumber, new { data_val = "false" })
@Html.HiddenFor(model => model.Region, new { data_val = "false" })
@Html.HiddenFor(model => model.INDDist, new { data_val = "false" })
@Html.HiddenFor(model => model.LastChangeDate, new { data_val = "false" })
@Html.HiddenFor(model => model.LastChangeOperator, new { data_val = "false" })
@Html.HiddenFor(model => model.EditTaxId, new { data_val = "false" })
@Html.HiddenFor(model => model.ParentId, new { data_val = "false" })
@Html.HiddenFor(model => model.IsSubstat, new { data_val = "false" })
@Html.HiddenFor(model => model.ID, new { data_val = "false" })
呈现隐藏部分
<input data-val="false" data-val-date="The field CreatedDate must be a date." id="CreatedDate" name="CreatedDate" type="hidden" value="" />
<input data-val="false" id="CreatedOperator" name="CreatedOperator" type="hidden" value="" />
<input data-val="false" id="Region" name="Region" type="hidden" value="NM-834" />
<input data-val="false" id="INDDist" name="INDDist" type="hidden" value="834" />
<input data-val="false" data-val-date="The field LastChangeDate must be a date." data-val-required="The LastChangeDate field is required." id="LastChangeDate" name="LastChangeDate" type="hidden" value="4/8/2015 10:43:30 AM" />
<input data-val="false" id="LastChangeOperator" name="LastChangeOperator" type="hidden" value="TYPCLS" />
<input data-val="false" data-val-required="The EditTaxId field is required." id="EditTaxId" name="EditTaxId" type="hidden" value="False" />
<input data-val="false" data-val-number="The field ParentId must be a number." id="ParentId" name="ParentId" type="hidden" value="" />
<input data-val="false" data-val-required="The IsSubstat field is required." id="IsSubstat" name="IsSubstat" type="hidden" value="False" />
<input data-val="false" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="0" />
控制器
[HttpPost]
[MonetAuthorize]
public ActionResult Create(AgentTransmission agenttransmission, bool andAddAgent = false)
{
.
.
.
//Determine if this is first POST or not
if (agenttransmission.ID > 0)
{
db.Entry(agenttransmission).State = EntityState.Modified;
}
else
{
db.AgentTransmission.Add(agenttransmission);
}
db.SaveChanges();
//Send back to view if errors pressent
if (!String.IsNullOrWhiteSpace(errorMsg))
{
return View(agenttransmission);
}
}
编辑
如果我删除 HiddenFor
帮助程序并简单地 cut/paste 呈现的 input
标记,我就能够捕获正确的 PK 值。然而,这有点 hacky 所以我希望找到一个更优雅的解决方案(如果可能的话)
<input data-val="false" id="ID" name="ID" type="hidden" value="@Model.ID" />
这里经常会问到这个问题的变体。基本上它归结为 ModelState
对象以及它的值覆盖视图实际模型上的值的事实。当您 post 表单时,0
在 ModelState
对象中设置为您的 ID
属性。在 post 操作中,您保存实体,这会导致其 ID
属性 更新,但 0
仍在 ModelState
中。当您 return 视图时,0
再次设置为 ID
的值,因为,这又是 ModelState
.
中的值
以这种方式工作的原因最好用一个例子来解释。假设您有一个正在编辑现有实体的表单,该实体的 Name
属性 设置为 "Foo"。用户在表单中将其更改为 "Bar" 然后 posts 表单。但是,他们忘记填写必填字段,因此错误阻止了他们的更新被保存。此时应该发生什么?如果我们使用模型值,Name
字段将重置为 "Foo"。但是,如果使用ModelState
,则Name
字段保留用户对"Bar"的修改。在后一种情况下,他们只是修复错误并再次 post 。在前者中,他们必须重新制作之前对表单所做的所有更改,这显然是非常糟糕的用户体验。
现在,关于如何解决这个问题。最好的方法是遵循 PRG 模式 (Post-Redirect-Get)。如果提交很好,并且您成功保存了更改,那么就不要 return 视图,即使您希望用户能够立即进行其他更改。如果你想要那样,只需重定向回相同的操作,但重定向过程足以清除 ModelState
以便用户现在与从数据库中提取的更新模型进行交互。
如果那不可行,那么您可以简单地清除 ModelState
。我建议不要完全清除它,因为您可能会导致一些非常真实的用户感到沮丧,如上例所述。如果您确实无法进行重定向,请尝试仅清除您真正需要的 ModelState
中的值。
ModelState.Remove("ID");
运行 在我的 ASP MVC 4 站点中遇到了一个奇怪的问题。如果用户打开 create
表单并尝试将代理添加到我们的数据库,我们自然会首先对字段进行 运行 验证。如果出现错误,我们将代理保存为未完成状态,并将用户重定向回创建页面。
用户尝试重新保存代理时出现错误。在第一个 post-back 中,代理被保存到数据库中并生成一个 PK。然而,在第二个 post-back 中,PK 被发送到服务器,其值为 0
而不是刚刚自动生成的值。
我在 Create
视图上添加了一个 HiddenFor
,但是每次呈现的值都是 0
。
我还逐步检查了代码,以确保在调用 .Save
后生成 PK,在调用 return View
时仍然存在,并确保 Model.ID
属性 在呈现视图时包含相同的值。
无论如何,如果我右键单击页面并查看源代码,隐藏字段呈现如下:
<input data-val="false" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="0" />
型号
public partial class AgentTransmission
{
public int ID { get; set; }
.
.
.
}
查看
@model MonetModelFromDb.Models.AgentTransmission
@{
ViewBag.Title = "Create new Agent";
}
@Html.HiddenFor(model => model.CreatedDate, new { data_val = "false" })
@Html.HiddenFor(model => model.CreatedOperator, new { data_val = "false" })
@Html.HiddenFor(model => model.ReferenceNumber, new { data_val = "false" })
@Html.HiddenFor(model => model.Region, new { data_val = "false" })
@Html.HiddenFor(model => model.INDDist, new { data_val = "false" })
@Html.HiddenFor(model => model.LastChangeDate, new { data_val = "false" })
@Html.HiddenFor(model => model.LastChangeOperator, new { data_val = "false" })
@Html.HiddenFor(model => model.EditTaxId, new { data_val = "false" })
@Html.HiddenFor(model => model.ParentId, new { data_val = "false" })
@Html.HiddenFor(model => model.IsSubstat, new { data_val = "false" })
@Html.HiddenFor(model => model.ID, new { data_val = "false" })
呈现隐藏部分
<input data-val="false" data-val-date="The field CreatedDate must be a date." id="CreatedDate" name="CreatedDate" type="hidden" value="" />
<input data-val="false" id="CreatedOperator" name="CreatedOperator" type="hidden" value="" />
<input data-val="false" id="Region" name="Region" type="hidden" value="NM-834" />
<input data-val="false" id="INDDist" name="INDDist" type="hidden" value="834" />
<input data-val="false" data-val-date="The field LastChangeDate must be a date." data-val-required="The LastChangeDate field is required." id="LastChangeDate" name="LastChangeDate" type="hidden" value="4/8/2015 10:43:30 AM" />
<input data-val="false" id="LastChangeOperator" name="LastChangeOperator" type="hidden" value="TYPCLS" />
<input data-val="false" data-val-required="The EditTaxId field is required." id="EditTaxId" name="EditTaxId" type="hidden" value="False" />
<input data-val="false" data-val-number="The field ParentId must be a number." id="ParentId" name="ParentId" type="hidden" value="" />
<input data-val="false" data-val-required="The IsSubstat field is required." id="IsSubstat" name="IsSubstat" type="hidden" value="False" />
<input data-val="false" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="0" />
控制器
[HttpPost]
[MonetAuthorize]
public ActionResult Create(AgentTransmission agenttransmission, bool andAddAgent = false)
{
.
.
.
//Determine if this is first POST or not
if (agenttransmission.ID > 0)
{
db.Entry(agenttransmission).State = EntityState.Modified;
}
else
{
db.AgentTransmission.Add(agenttransmission);
}
db.SaveChanges();
//Send back to view if errors pressent
if (!String.IsNullOrWhiteSpace(errorMsg))
{
return View(agenttransmission);
}
}
编辑
如果我删除 HiddenFor
帮助程序并简单地 cut/paste 呈现的 input
标记,我就能够捕获正确的 PK 值。然而,这有点 hacky 所以我希望找到一个更优雅的解决方案(如果可能的话)
<input data-val="false" id="ID" name="ID" type="hidden" value="@Model.ID" />
这里经常会问到这个问题的变体。基本上它归结为 ModelState
对象以及它的值覆盖视图实际模型上的值的事实。当您 post 表单时,0
在 ModelState
对象中设置为您的 ID
属性。在 post 操作中,您保存实体,这会导致其 ID
属性 更新,但 0
仍在 ModelState
中。当您 return 视图时,0
再次设置为 ID
的值,因为,这又是 ModelState
.
以这种方式工作的原因最好用一个例子来解释。假设您有一个正在编辑现有实体的表单,该实体的 Name
属性 设置为 "Foo"。用户在表单中将其更改为 "Bar" 然后 posts 表单。但是,他们忘记填写必填字段,因此错误阻止了他们的更新被保存。此时应该发生什么?如果我们使用模型值,Name
字段将重置为 "Foo"。但是,如果使用ModelState
,则Name
字段保留用户对"Bar"的修改。在后一种情况下,他们只是修复错误并再次 post 。在前者中,他们必须重新制作之前对表单所做的所有更改,这显然是非常糟糕的用户体验。
现在,关于如何解决这个问题。最好的方法是遵循 PRG 模式 (Post-Redirect-Get)。如果提交很好,并且您成功保存了更改,那么就不要 return 视图,即使您希望用户能够立即进行其他更改。如果你想要那样,只需重定向回相同的操作,但重定向过程足以清除 ModelState
以便用户现在与从数据库中提取的更新模型进行交互。
如果那不可行,那么您可以简单地清除 ModelState
。我建议不要完全清除它,因为您可能会导致一些非常真实的用户感到沮丧,如上例所述。如果您确实无法进行重定向,请尝试仅清除您真正需要的 ModelState
中的值。
ModelState.Remove("ID");