[英]Passing two or more parameters from model view to controller in EF Core / ASP.NET Core MVC application
[英]Why is my ASP.NET Core 3.1 controller automatically assigning an ID to an EF Core model that was returned from a view?
这有点奇怪,我 90% 确定这是一个错误,但你永远不知道。
我正在为学生提供辅导课程,我试图让他们了解如何将 EF Core 与 ASP.NET Core 3.1 结合使用。 由于他们是初学者,我不想跳到为每个视图编写模型,以去除视图不需要的所有 EF Core 实体数据。 相反,我只是让他们将实体模型类直接传递给视图,并在他们从视图POST
时将其直接传递回控制器。
我试图向他们展示如何向Review
实体添加Comment
,但我遇到了一个奇怪的问题。
鉴于以下GET
操作:
[HttpGet]
public IActionResult Write([FromRoute] int id)
{
return View(new Comment
{
ReviewId = id
});
}
以及以下视图:
@model Comment
@{
ViewBag.Title = "Add a comment";
}
<nav>
<a asp-controller="Home" asp-action="Index">Back</a>
</nav>
<h1>ID: @Model.Id</h1>
<form method="post" asp-controller="Comments" asp-action="Write">
@Html.HiddenFor(c => c.ReviewId)
@Html.LabelFor(c => c.Content)
@Html.TextAreaFor(c => c.Content)
<button type="submit">Submit</button>
</form>
Comment
的 ID 会自动设置为我数据库中的最后一个 ID,即使它没有被 View 修改或由GET
方法设置,只要它到达我的控制器的POST
操作。
[HttpPost]
public IActionResult Write(Comment model)
{
// Using a debugger, "model" has an ID equal to that of the
// last Comment in my database as soon as we enter this method...
var review = _context.Reviews
.Include(r => r.Comments)
.FirstOrDefault(r => r.Id == model.ReviewId);
if (review != null)
{
review.Comments.Add(model);
_context.Update(review);
_context.SaveChanges(); // <-- Causes a primary key conflict error because of "model.Id"'s value
}
}
我对这种行为感到有些困惑,但我并没有永远将 EF Core 模型传递给控制器(我们通常编写特定于视图的模型来去除我工作的不需要的数据),所以这可能是正常行为? 但是,感觉就像我的错误。
通过简单地明确绑定我想要的属性,我找到了一个“修复”:
[HttpPost]
public IActionResult Write([Bind("Content, ReviewId")]Comment model)
但是我想知道为什么model
的Id
属性在它到达我的控制器后会自动设置为最后一个Comment
实体的Id
。
在进一步摆弄之后,我找到了问题的根源。
TL; 博士:
这解决了我的问题。
[HttpPost]
public IActionResult Write([FromForm]Comment model)
解释:
我完全忘记将[FromForm]
添加到我的控制器的POST
操作中,因此框架没有将正确的数据放在正确的字段中。 我猜它是使用默认控制器路由端点映射的{id?}
部分并将此值分配给model.Id
,这会在向Review
添加多个Comment
时导致问题,因为 URL 的 Route id
值是父级Review
的Id
,而不是Comment
的,当您将多个Comment
添加到单个Review
时,此值保持不变。
这意味着向第一个Review
对象添加第二个Comment
会导致主键冲突,因为Review
的Id
是“1”,第一个Comment
的Id
也是“1”,URL 将是/write/1
,将第二个Comment
的Id
分配给“1”,导致密钥冲突。
这也是为什么model.Id
的值在碰到我的控制器后似乎立即发生变化的原因。 因为它是。 控制器只是使用它认为最好的方法从请求中获取数据,但它做错了。
当我添加一个隐藏的<input>
标签以确保将Id
强制设置为 0 不会解决问题时,我意识到绑定有问题。
我之前在视图中添加了以下行,以确保我的Id
在视图中设置为“0”:
<h1>ID: @Model.Id</h1>
但是隐藏的<input>
标签有一个与Review
的Id
匹配的值,而不是“0”。 所以我再次研究数据绑定,并在 MSDN 上找到了这个页面,它涵盖了Binding source parameter inference的主题,其中指出框架将尝试使用它认为最好的方法,而没有给出明确的绑定源属性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.