在 http post 控制器中使用实体 属性 与分配更好吗?

Is it better to use entity property vs assignments in http post controller?

假设我们有一个从另一个应用程序返回 HttpPost 对象的 aspnet core2 控制器。 我正在使用 Entity framework 内核,这两种方式都有效,但我只是对最佳实践和性能感到好奇,想知道该使用什么?

[HttpPost]
        public Message Post([FromBody] Message message)
        {
            // my old code
            // var msg = new Message { Owner = message.Owner, Text = message.Text };
            //db.Messages.AddAsync(msg)

            var msgEntity = db.Messages.Add(message).Entity;
            db.Messages.AddAsync(message);
            db.SaveChangesAsync();

            return message;

        }

当我使用 Entity 时发生了一些奇怪的事情,它更改了自动递增的 ID 参数 {Id:1002},而在我添加它之前,下一个连续的 ID 应该是 {Id:11}

永远不要直接从请求中保存任何内容。这是您的最佳做法。前面的代码之所以优越,唯一的原因是您明确选择了实际保留的发布值,而不是盲目地保存用户决定直接发送到您的数据库的任何内容。

更好的方法是实际使用视图模型来接受用户输入。例如:

public class MessageViewModel
{
    public int OwnerId { get; set; }

    [Required]
    public string Text { get; set; }
}

然后,您接受它作为参数,并将其映射到您的实体:

public async Task<IActionResult> Post([FromBody]MessageViewModel model)
{
    if (ModelState.IsValid)
    {
        var message = new Message
        {
            Owner = db.Users.Find(model.OwnerId),
            Text = model.Text
        };
        db.Messages.Add(message);
        await db.SaveChangesAsync();

        return Ok(message);
    }

    return BadRequest(ModelState);
}

我在上面的代码中做了一些重要的更改:

  1. 添加了条件以在继续保存之前检查 ModelState.IsValid 是否为真。正如您现在所拥有的那样,您只是在盲目地尝试保存传入的任何内容,即使这样做最终会引发数据库异常,因为缺少所需的值等等。

  2. 有了这个条件,您现在需要处理出现问题的场景。更有可能的是,您需要 return 某种错误对象,以帮助客户解决问题。实际上只是 return ModelState 是很常见的,这将序列化其验证错误列表,但您可能还想做其他事情。

  3. 由于这现在可​​能需要 returning 两种不同的对象类型,因此动作签名已更改为 return IActionResult。通常最好一直使用 return 类型。基本上,它是一个包罗万象的东西,而像 Message 这样的特定 return 很容易超出它的用处,需要您继续并稍后更改它。当绝对没有任何类型的失败机会时,你真的应该只 return 特定类型,即响应将 always 是 200 OK。然而,这种情况很少见。

  4. 视图模型只接受所有者的 id,而不是完整的(大概 ApplicationUser)对象。接受 Message 并直接保存它也存在同样的问题。恶意用户可以篡改 Owner 对象属性,从而允许他们潜在地更改各种不应更改的内容。通常,您应该始终着手让用户操作尽可能少的数据。您公开的任何内容都应该有意,充分理解其中的含义。因为,我们现在只接受一个 id,然后我们需要查找具有该 id 的用户,以在实体上设置 Owner 属性。如果您碰巧有一个显式外键 属性,例如 Message 上的 OwnerId,您可以直接设置它。

  5. 已将 AddAsync 更改为 Add。根据文档,您几乎不应该使用 AddAsync。它仅出于您不太可能遇到的非常特定的目的而存在。建议始终使用 Add,除非您有充分的理由。

  6. 已将 await 添加到 SaveChangesAsync 调用。始终等待异步操作,除非该操作的完成与任何事情无关。在这里绝对不是这样。 SaveChangesAsync 可以引发异常,您的应用程序需要处理的异常。不等待,基本上就是吞下这些,让代码愉快地继续下去,就好像没有问题一样。虽然这听起来像是积极的,但绝对不是。不等待也可能导致其他问题,例如上下文可能会在调用完成之前被处理掉。

  7. 由于我们现在正在等待 SaveChangesAsync,因此必须将 async 添加到方法签名中,并且您必须 return 一个 Task<IActionResult>