繁体   English   中英

为什么我的 ASP.NET Core 3.1 控制器会自动为从视图返回的 EF Core 模型分配 ID?

[英]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)

但是我想知道为什么modelId属性在它到达我的控制器后会自动设置为最后一个Comment实体的Id

在进一步摆弄之后,我找到了问题的根源。


TL; 博士:

这解决了我的问题。

[HttpPost]
public IActionResult Write([FromForm]Comment model)

解释:

我完全忘记将[FromForm]添加到我的控制器的POST操作中,因此框架没有将正确的数据放在正确的字段中。 我猜它是使用默认控制器路由端点映射的{id?}部分并将此值分配给model.Id ,这会在向Review添加多个Comment时导致问题,因为 URL 的 Route id值是父级ReviewId ,而不是Comment的,当您将多个Comment添加到单个Review时,此值保持不变。

这意味着向第一个Review对象添加第二个Comment会导致主键冲突,因为ReviewId是“1”,第一个CommentId也是“1”,URL 将是/write/1 ,将第二个CommentId分配给“1”,导致密钥冲突。

这也是为什么model.Id的值在碰到我的控制器后似乎立即发生变化的原因。 因为它是。 控制器只是使用它认为最好的方法从请求中获取数据,但它做错了。

当我添加一个隐藏的<input>标签以确保将Id强制设置为 0 不会解决问题时,我意识到绑定有问题。

我之前在视图中添加了以下行,以确保我的Id在视图中设置为“0”:

<h1>ID: @Model.Id</h1>

但是隐藏的<input>标签有一个与ReviewId匹配的值,而不是“0”。 所以我再次研究数据绑定,并在 MSDN 上找到了这个页面,它涵盖了Binding source parameter inference的主题,其中指出框架将尝试使用它认为最好的方法,而没有给出明确的绑定源属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM