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

时间:2017-11-14 20:43:35

标签: entity-framework-core asp.net-core-2.0

所以说我们有一个aspnet core2控制器,它从另一个应用程序返回HttpPost对象。 我使用实体框架核心,两种方式都可以工作,但我只是对最佳实践和性能感到好奇,知道要使用什么?

[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}

1 个答案:

答案 0 :(得分:2)

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

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

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. 使用此条件,您现在需要处理出现问题的情况。更有可能的是,您希望返回某种错误对象,以帮助客户解决问题。实际上只返回{​​{1}}是常见的,它将序列化其验证错误列表,但您可能也想做其他事情。

  3. 由于现在需要返回两种不同的对象类型,因此操作签名已更改为返回ModelState。通常最好只使用这种返回类型。它基本上是一个包罗万象,而像IActionResult这样的特定回报很容易超过它的用处,要求你继续前进并稍后改变它。你应该只返回特定类型,当绝对没有任何类型的失败时,即响应总是 200 OK。但是,这种情况很少见。

  4. 视图模型只接受所有者的ID,而不是完整的(可能是Message)对象。接受ApplicationUser并直接保存它的同样问题也存在于此。恶意用户可以使用Message对象属性进行捏造,从而可以更改各种不应更改的内容。通常,您应该始终允许用户操作尽可能少的数据。您公开的任何内容都应该故意完全理解其含义。既然,我们现在只接受id,那么我们需要查找具有该id的用户,以在该实体上设置Owner属性。如果您碰巧有一个明确的外键属性,例如Owner上的OwnerId,您可以直接设置它。

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

  6. Add电话中添加了await。始终等待异步操作,除非操作完成对任何事情都没有影响。绝对不是这样的。 SaveChangesAsync可以引发异常,应用程序需要处理的异常。不等待它,基本上只是吞下这些并让代码愉快地继续,好像没有问题。虽然这听起来像是积极的,但绝对不是。还有其他问题可以由不等待引起,例如上下文可能会在通话结束前被处理掉。

  7. 由于我们现在正在等待SaveChangesAsync,因此必须将SaveChangesAsync添加到方法签名中,并且您必须返回async